CodeBetter.Com
CodeBetter.Com
RSS 2.0 via Feedburner
           Do you Twitter? Follow us @CodeBetter

Eric Wise

Business & .NET

"Coding to Interfaces"

Here's something I see time and time again from new entrants to the OO world in .NET.  If you want to make your code more stable, reliable, and extensible in certain aspects of enterprise systems you must embrace the concepts of coding to interfaces, not inheritance to keep yourself from digging into a nightmare down the road.  To better explain what I'm talking about, here's a very simple example to help show the concept:

Let's say you have a system with a method that performs a calculation.  What the calculation is doesn't really matter, it could be a sales tax calc, or anything really.  The important point is that currently your company supports this calculation in Ohio.  So let's make a theoretical function that takes two double inputs and in Ohio the rules are that they should be added together.  So we end up with this in our Ohio calculations class:

    Public Function myCalc(ByVal x As Double, ByVal y As Double) As Double

        Return x + y

    End Function

Now this is all well and good, but then the company expands into Michigan.  Luckily, Michigan handles this calculation the same way Ohio does.  So being a good little developer we decide to promote myCalc to an inheritable.

Public MustInherit Class Calculations

    Public Function myCalc(ByVal x As Double, ByVal y As Double) As Double

        Return x + y

    End Function

End Class

Wonderful, now the ohio and michigan classes can inherit from this file and both of them can share the code.  Everything is lovely and grand and we all pat ourselves on the back for not having written the code in two places!

However, our celebration is interrupted when the company again grows and moves into Indiana.  Indiana rules say that x + y is totally bogus, and it needs to be x * y.  Now we have a couple options:

  1. Start messing with our inheritance chain to try to override this function.
  2. Start putting conditional logic and edit the signature to take in state
  3. Code to an interface!

Numbers 1 and 2 have some pretty critical flaws.  When you start messing with the inheritence chain and putting in conditional logic you run the risk of breaking existing functionality and in the case of the signature edit we now have to modify all the consumers of our inheritable.  This doesn't seem so bad on this small scale, but imagine if you had all 50 states and each one had a different way of performing this calculation?  This is where coding to interfaces saves your butt!  What we want to ensure is that we encapsulate our differing logic in such a way that when a change or addition occurs we do not have to modify our existing, working code base at all.

How do we do this?  We take advantage of the power of inheritance and interfaces.  First we need an interface to enforce the method and an inheritable class that can hold a pointer to our interface.  Like so:

Public MustInherit Class StateStuff

    Protected calcs As ICalculations

End Class

 

Public Interface ICalculations

    Function myCalc(ByVal x As Double, ByVal y As Double) As Double

End Interface

So now we have required that every state can hold an ICalculation, the implementation of which we can define at will.  Now we need a couple classes that implement the interface that hold the differing logic.  Per our story above we have addition and multiplication.  So we'll do something like this:

Public Class AdditionCalculation

    Implements ICalculations

 

    Public Function myCalc(ByVal x As Double, ByVal y As Double) As Double Implements ICalculations.myCalc

        Return x + y

    End Function

End Class

 

Public Class MultiplicationCalculation

    Implements ICalculations

 

    Public Function myCalc(ByVal x As Double, ByVal y As Double) As Double Implements ICalculations.myCalc

        Return x * y

    End Function

End Class

Now for the states.  Per our story above we have 3 states and 2 cases, one case is addition and one is multiplication.  Therefore we can go a couple routes.  One route is to make the various states have their own classes.  This has the downside of creating a lot of classes, but ends up being fairly clean if there are a lot of differences between states.  In the constructor of the state class we will set the ICaculation that state should use like so:

Public Class OhioStuff

    Inherits StateStuff

 

    Public Sub New()

        calcs = New AdditionCalculation

    End Sub

End Class

 

Public Class IndianaStuff

    Inherits StateStuff

 

    Public Sub New()

        calcs = New MultiplicationCalculation

    End Sub

End Class

Notice how our constructor properly lets the type of calculation that should be performed.  All we would need to expose this to the system is an accessor method!  Even more powerful, we can now create methods elsewhere in the system that take in the generic StateStuff as a parameter, and inside that method we can call calcs.myCalc and it will always perform the proper calculation regardless of what state is in use.  No conditional logic needed!  That's flexibility!  In addition, we can guarantee that if we add new types of calculations, like subtraction, and new states that the changes will never break existing code!

 

The second way to approach this from the end class model is to have one generic class and a factory that assigns the various interface classes based on whatever conditional logic you have.  The factory method would look something like this:

Public Class StateStuff

    Public calcs As ICalculations

 

End Class

 

Public Class StateFactory

    Public Shared Function GetState(ByVal stateName As String) As StateStuff

        Dim s As New StateStuff

 

        Select Case stateName

            Case "OH" Or "MI"

                s.calcs = New AdditionCalculation

            Case "IN"

                s.calcs = New MultiplicationCalculation

        End Select

 

        Return s

    End Function

End Class

So at the end of the call you get a state class with its custom calculation implementation.  A new state or interface class would require editing the factory, but all the other code should be unchanged and protected from breakage!



Comments

Jason Haley said:

# December 16, 2005 8:38 AM

Raymond Lewallen said:

Great explanation of the abstract factory pattern.
# December 16, 2005 9:42 AM

Kory said:

Is this not also an example of the strategy pattern?
# December 16, 2005 9:57 AM

Eric Wise said:

The intent was not so much to tie this down to a pattern but to explain what the real world implementation (no pun intended) of interfaces in .NET is designed to handle.

Interfaces and coding to them can be a great solution to a specific problem set you encounter in reality. Combined with proper use of inheritance you can create a damn flexible and powerful system. Overuse or use when not appropriate like in all concepts/patterns is just as bad as not using them at all. =)
# December 16, 2005 10:11 AM

slaroche@pcms.com said:

Coding to Interfaces also has the side effect of making your code much easier to test.
# December 16, 2005 10:32 AM

Raymond Lewallen said:

Kory, yes, the first example is strategy pattern. 2nd is abstract factory.
# December 18, 2005 12:24 AM

the blog of michael eaton said:

# December 21, 2005 10:01 AM

steveOH said:

If you change 'Return x + y' to 'Return x * y' in the mustinherit class, then it changes for all 50 classes that inherit it.

Why do you say in option 1) above that this is not a practical solution?

# August 12, 2007 5:23 AM

heinz said:

sorry, i don't get it. You have to change city classes that need the mutiplication instead of addition  calc anyway (all 30 or so of them), so why not simply put an extra multiplication calc function in the original class and

use that in the ciyies that need it?

There's something odd about interfaces, I don't think they are necessary at all! sorry woah up there boy!!! Well - the reasons to use them are seemingly contrived based on some paradigm that is only a 'wish' to lay down rules and in fact one could invent another paradigm and lay down that rule too.

Also, they throw up all sorts of logical gymnastics in code that we could all do without. (interfaces containing same method names etc etc) Do we really have to listen to the young man speaking martian just to appear 'clever', when we actaully need simple logic that is effective.

# August 12, 2007 5:59 AM

Eric Wise said:

Steve and Heinz:

The purpose of this exercise is to demonstrate very simply the concept of what makes interfaces powerful.  Imagine for a moment that each state had it's own separate laws and ways for calculating something.  Would you create 50 methods?  You certainly couldn't overload them easily, and then you end up with 50 methods in a single class that is a huge burden for others to read, parse, and test.

By creating a common interface, you have the freedom to keep all the files separate, testable, and readable.  As you add complexity to the methods the payout is larger and larger.

Hell, we *could* do all 50 states in one gigantic super method with a ton of if statements, but don't ask me to debug that ball of mud for you ;)

So yes, I agree that you really don't need interfaces to have a running program, but as you add complexity it becomes a much more elegant and maintainable solution.

# August 13, 2007 9:25 AM

Heinz said:

Sorry to keep on about this (I am at the limits of my intelligence here!) but why would just one class have 50 methods?

The 50 methods would have to be written somewhere, there's no getting round that. The interface does nothing except

write the one name of a method that must be overriden 50 times when it is used, so I suppose its more readable to look

at the interface to see whats happening lower down. But it brings nothing to the table except maybe clarity.

Also, we could use a top level abstract or ordinary class instead of an interface. But, there again, why write the method

once at all? It does nothing except allow a programmer to read it. It brings nothing to the table at all except the ability

to see a method name in a top class or interface. Well, that could be documented or commented with less hassle.

Below I have written the first version using an interface and the second version using a top level class and the third version

using neither. The easiest and fastest is the version that uses neither an interface nor a top level class!!

There may be something I am missing, for example there may be a 'collection' or 'array' example that is better because

each interface referred object 'knows' what the correct method to call in a looping through hundreds of objects.

I agree that a top level class common method is very useful when most of the sub classes use the same method implementation.

Then, indeed, we don't have to write it out 50 times. But where does an interface help there? It doesn't because we have

to implement the method ourselves 50 times. Better no interface at all.

Also, if two years later we decide that we need another method in the interface, and we add one more methodd, then every

single client of that interface is busted - runtime errors everywhere! So interfaces are actually becoming terrible things,

and should never have been invented???

************* using an interface

using System;

public interface TopStateClass

  {

 void Calc(); // a method thats going to be overridden for 50 different US states

  }

public class Ohio : TopStateClass

{

   public  void Calc()

   {

       Console.WriteLine(" the Ohio calc is:  " + Math.Sin(22.2));

   }

}

public class Montana : TopStateClass

{

   public  void Calc()

   {

       Console.WriteLine(" the Montana calc is:  " + Math.Tan(44.3));

   }

}

public class LetsDoTheCalcs

{

   static void Main(string[] args)

   {

       TopStateClass oh = new Ohio();

       oh.Calc() ;

       TopStateClass mo = new Montana();

       mo.Calc();

       Console.ReadLine();

   }

}

************* using a high up in hierachy class:

using System;

public class TopStateClass

  {

      // we declare one method virtual so that the Executive class can

      // override it.

      public virtual void Calc()

      {

          Console.WriteLine(" sorry - you need to override this for a nam state ");

      }

  }

public class Ohio : TopStateClass

{

   // the override keyword indicates we want new logic behind the Calc method.

   public override void Calc()

   {

       Console.WriteLine(" the Ohio calc is:  " + Math.Sin(22.2));

   }

}

public class Montana : TopStateClass

{

   // the override keyword indicates we want new logic behind the Calc method.

   public override void Calc()

   {

       Console.WriteLine(" the Montana calc is:  " + Math.Tan(44.3));

   }

}

public class LetsDoTheCalcs

{

   static void Main(string[] args)

   {

       Ohio oh = new Ohio();

       oh.Calc() ;

       Montana mo = new Montana();

       mo.Calc();

       Console.ReadLine();

   }

}

*********************** But you don't need this top class or the interface at all, see this:

using System;

public class Ohio

{

   // the override keyword indicates we want new logic behind the Calc method.

   public  void Calc()

   {

       Console.WriteLine(" the Ohio calc is:  " + Math.Sin(22.2));

   }

}

public class Montana

{

   // the override keyword indicates we want new logic behind the Calc method.

   public  void Calc()

   {

       Console.WriteLine(" the Montana calc is:  " + Math.Tan(44.3));

   }

}

public class LetsDoTheCalcs

{

   static void Main(string[] args)

   {

       Ohio oh = new Ohio();

       oh.Calc() ;

       Montana mo = new Montana();

       mo.Calc();

       Console.ReadLine();

   }

}

# August 15, 2007 5:02 AM

Eric Wise said:

You are missing the point that you can only inherit from one class but you can implement many interfaces.  That's one reason why the abstract class falls short in a lot of real world scenarios.

I highly recommend you read Head First Object Oriented Analysis and Design.  The examples are in java, but it's a fantastic book for explaining OO concepts like interfaces and when they are appropriate.

# August 16, 2007 12:31 PM

heinz said:

thanks for advice. I'll give Head First a try.

# August 18, 2007 10:58 AM

Leave a Comment

(required)  
(optional)
(required)  

Enter the numbers above:
Add
Check out Devlicio.us!