Reputation: 402
After having to refactor the inheritance chain in my current project for the third time I googled "Inheritance sucks" and found that the problem I'm having is not uncommon and composition is a recommended alternative solution.
I understand how you can use composition to add behavior in form of functions, but I'm having problems to come up with ways to add properties by the same means.
Let's say I want to model tree nodes. Every node has at least two properties: name and description.
class Node {
public string Name { get; set; }
public string Description { get; set; }
}
Other more specific nodes would inherit those properties, like so:
class StructuredNode : Node {
public List<Node> Children { get; set; }
}
How could I achieve similar re-usability of the property code without relying on inheritance and the problems that come with it?
Is there a design pattern for this or do I have to use inheritance in such a case?
Thanks in advance!
Edit: Examples for the position "composition over inheritance":
Upvotes: 3
Views: 1752
Reputation: 430
Have an INode interface, which encapsulates common properties.
This way you should have auto properties, then avoid putting logic in properties' getter and setter, because you can not reuse this logic.
Then repeating auto property definitions is not important and does not affect reusability.
If you need property change notification, it is better to use interceptor libraries such as postsharp.
Upvotes: 0
Reputation: 13763
How could I archive similar re-usability of the property code without relying on inheritance and the problems that come with it?
The alternative to using inheritance is either interfaces or composition. However, for properties specifically, you're a bit stuck.
Dictionary<string,string>
). There may be some contrived method to technically make it work, but it won't be a good approach.Interfaces + extension methods.
Extension methods can be used here to replace the reusable logic that you'd find in an inherited base class.
There is one drawback to this: The properties that you wish to access inside the extension methods need to be part of the interface contract and publically accessible.
Other than this drawback, it ticks the box on every other requirement you have.
First, an inheritance-based example:
public class Property
{
public string Name { get; set; }
public string Value { get; set; }
}
public class PropertyComposedObject
{
public List<Property> Properties { get; set; }
public Property GetProperty(string name)
{
return this.Properties.SingleOrDefault(x => x.Name == name);
}
}
public class Person : PropertyComposedObject
{
}
If we were to use an interface instead, we would lose access to benefits such as a shared GetNode(string)
method. You could add it as part of the interface, but each implementing class would then be responsible for implementing that method (leading you to copy/paste the same methods all over the place).
An interface example without extension methods:
public class Property
{
public string Name { get; set; }
public string Value { get; set; }
}
public interface IPropertyComposedObject
{
List<Property> Properties { get; set; }
Property GetProperty(string name);
}
public class Person : IPropertyComposedObject
{
public List<Property> Properties { get; set; }
public Property GetProperty(string name)
{
return this.Properties.SingleOrDefault(x => x.Name == name);
}
}
But extension methods allows us to define the reusable method once but still access it from every class that implements the interface:
public class Property
{
public string Name { get; set; }
public string Value { get; set; }
}
public interface IPropertyComposedObject
{
List<Property> Properties { get; set; }
}
public class Person : IPropertyComposedObject
{
public List<Property> Properties { get; set; }
}
public static class IPropertyComposedObjectExtensions
{
public Property GetProperty(this IPropertyComposedObject obj, string name)
{
return obj.Properties.SingleOrDefault(x => x.Name == name);
}
}
Upvotes: 1
Reputation: 16049
My attempt to minimize code duplication:
interface INodeProperties
{
string Name { get; set; }
string Description { get; set; }
}
class NodeProperties : INodeProperties
{
public string Name { get; set; }
public string Description { get; set; }
}
interface INode
{
INodeProperties NodeProps { get; set; }
}
class Node : INode
{
public INodeProperties NodeProps { get; set; } = new NodeProperties();
}
interface IStructuredNode
{
List<Node> Children { get; set; }
}
class StructuredNode: INode, IStructuredNode
{
public INodeProperties NodeProps { get; set; } = new NodeProperties();
public List<Node> Children { get; set; }
}
Downside: One more "hop" to get to the actual Properties ... :(
Upvotes: 0
Reputation: 176886
Rather then depending on class , you should depend son abstraction (this also one part of making use of composition) so for you case you should do like this
public interface INode {
string Name { get; set; }
string Description { get; set; }
}
class Node : INode {
public string Name { get; set; }
public string Description { get; set; }
}
class StructuredNode : INode {
public string Name { get; set; }
public string Description { get; set; }
public List<INode> Children { get; set; }
}
or you can also do this
//this is something similar to decorator pattern.
class StructuredNode {
private readonly INode _node;
public StructureNode(INode node)
{
_node = node;//now you can reuse function of any class which implements INode
}
public List<INode> Children { get; set; }
}
you should do like this also later on
List<Node> nodes = List<Node>();
StructuredNode sNode = new StructuredNode();
sNode.Children = nodes;
this is possible as all is based on abstraction. and all implementation now make use of INode
or
other solution suggested you in comment is make use of Decorator pattern
. If you just want to extend you class without modifying it.
Upvotes: 2