Search code examples
c#inheritanceinterfacesolid-principles

Using C# interfaces while maximizing code reuse


So say I had a C# class,

class Foo : Bar, IBar, IBar2
{

}

... where Bar is a class, and IWhatever and IFine are interfaces. I plan to use a similar implementation of IWhatever and IFine across multiple classes- the only sane way I can see to encapsulate this code and reuse it across all the classes is to make a class Whatever that inherits from IWhatever and Fine that inherits from IFine and make them members of the class that implements those interfaces and then call their members in the members implemented from the interfaces, like so:

class Foo : Bar, IWhatever, IFine
{
   IWhatever mWhatever;
   IFine mFine;
   Foo()
   {
      mWhatever = new Whatever();
      mFine = new Fine();
   }
   // from IWhatever
   void Kick()
   {
      mWhatever.Kick();
   }
   // from IFine
   void Punch()
   {
      mFine.Punch();
   }
}

Am I doing it right? Is there a better way?


Solution

  • When you are trying to implement the same multiple implementations across many classes via inheritance, that is a design smell. C#'s lack of multiple (class) inheritance is actually an asset, not a liability in this regard, because it enforces the single-responsibility principle.

    Think of it this way: the two types of abstractions available in C# have two complementary forms of representing real-life entities.

    • Abstract class: "Is a"
    • Interface: "Does something" or "Has such and such qualities/behavior"

    The implication of this is that interfaces should almost always be "hollow" because we assume that even though different classes may implement the same interface, it is likely that each one will do it differently.

    Be careful about the use of inheritance. Remember the sage wisdom of the GOF:

    Favor object composition over inheritance.

    So getting back to the problem you're trying to solve, it might be better to do this:

    public class Foo : Bar
    {
       public IWhatever Whatever
       {
          get;
          private set;
       }
    
       public IFine Fine
       {
          get;
          private set;
       }
    
       public Foo(IWhatever whatever, IFine fine)
       {
          Whatever = whatever;
          Fine = fine;
       }
    }
    

    Now use an IOC container to inject concrete implementations of those interfaces. This is my humble recommendation.