Question

Suppose I have a various entities in my model (using EF), say User, Product, Invoice and Order.

I am writing a user control that can can print the summaries of entity objects in my application where the entities belong to pre-decided set, in this case I say that summaries of User and Product can be summarized.

The summaries will all only have a ID and a description, so I create a simple interface for this:

 public interface ISummarizableEntity {     
       public string ID { get; }    
       public string Description { get; } 
 }

Then for the entities in question, I create a partial class that implements this interface:

public partial class User : ISummarizableEntity
{
    public string ID
    {
        get{ return UserID.ToString(); }
    }

    public string Description 
    {
        get{ return String.Format("{0} {1} is from {2} and is {3} years old", FirstName, LastName, Country, Age); }
    }
}

public partial class Product: ISummarizableEntity
{
    public string ID
    {
        get{ return ProductID.ToString(); }
    }

    public string Description 
    {
        get{ return String.Format("{0} weighs {1}{2} and belongs in the {3} department", ProductName, WeightValue, WeightUnit, Department); }
    }
}

This way my user control / partial view can just bind to any collection of ISummarizableEntity and doesn't need to be interested in the source at all. I have been told that interfaces shouldn't be used as datatypes but I didn't get more information than that. As far as I can see, although interfaces normally describe behavior, just using properties isn't an anti-pattern in itself as properties are just syntactic sugar for getters/setters anyway.

I could create a concrete datatype and map from the entities to that but I can't see the benefit. I could make the entity objects inherit from an abstract class and then define the properties but then I am locking the entities to no further use as we can't have multiple inheritance. I am also open to having any object being ISummarizableEntity if I wanted (obviously I would rename the interface)

The solution I am using in my mind is maintainable, extensible, testable and fairly robust. Can you see the anti-pattern here?

Was it helpful?

Solution

Interfaces don't describe behaviour. Quite the opposite, sometimes.

Interfaces describe contracts, such as "if I am to offer this object to any method that accepts an ISummarizableEntity, this object must be an entity that is able to summarize itself" -- in your case, that is defined as being able to return a string ID and a string Description.

That's a perfect use of interfaces. No anti-pattern here.

OTHER TIPS

You have chosen the better path for this design because you are defining a specific type of behavior that will be required of multiple, different, types of objects. Inheritance in this case would imply a common relationship between the classes that does not actually exist. In this case, composability is favored over inheritance.

Interfaces that only carry properties should be avoided since :

  • it obfuscates the intent : you solely need a data container
  • it encourages inheritance : probability that someone will mix concerns in the future
  • it prevents serialization

Here you are mixing two concerns :

  • summary as a data
  • summary as a contract

A summary is made of two strings : an id and a description. This is plain data :

public class Summary {
    private readonly string id;
    private readonly string description;
    public Summary(string id, string description) {
        this.id = id;
        this.description = description;
    }
    public string Id { get { return id; } }
    public string Description { get { return description; } }
}

Now that you have defined what a summary is you want to define a contract :

public interface ISummarizableEntity {
    public Summary GenerateSummary();
}

Note that using intelligence in getters is an anti-pattern and should be avoided : it should be located in functions instead. Here is how implementations look like :

public partial class User : ISummarizableEntity {
    public Summary GenerateSummary() {
        var id = UserID.ToString();
        var description = String.Format("{0} {1} is from {2} and is {3} years old", FirstName, LastName, Country, Age);
        return new Summary(id,description);
    }
}

public partial class Product : ISummarizableEntity {
    public Summary GenerateSummary() {
        var id = ProductID.ToString();
        var description = String.Format("{0} weighs {1}{2} and belongs in the {3} department", ProductName, WeightValue, WeightUnit, Department);
        return new Summary(id,description);
    }
}
Licensed under: CC-BY-SA with attribution
scroll top