Ruben Bohnet
Ruben Bohnet

Reputation: 402

How to include properties via composition?

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

Answers (4)

Polymorphic
Polymorphic

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

Flater
Flater

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.

  • Interfaces cannot contain a default implementation the same way that a base class can. So while you can enforce that your classes use the correct "composed property structure", you can't make reusable methods available without implementing them in every class that implements the interface (or can you? More after the break!)
  • Composition simply doesn't exist in C# in a way that you can add properties to a class on the fly (unless you are satisfied with a 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

Fildor
Fildor

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

Pranay Rana
Pranay Rana

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

Related Questions