Click here to Skip to main content
15,885,546 members
Articles / Programming Languages / C#
Article

Internal Implementer

Rate me:
Please Sign up or sign in to vote.
3.50/5 (4 votes)
19 Apr 2004CPOL4 min read 35.3K   40   23   2
A simple pattern for implementing interfaces across several class types in C#.

Introduction

While coding at work (Autoscribe), I needed to come up with a cross-cutting observer pattern. When I say 'cross-cutting', I mean that the mechanism had to work with several class types using interfaces.

What was needed, was an easy way to implement the interface in classes that have no base-type, or in classes that already have a base type. It also had to be relatively fool-proof to code with. I decide to present this as a pattern as this looks more imposing than an article entitled 'A Good Trick with Interfaces'.

Although I have presented this technique in C#, it would work just as well in any language that disallows multiple inheritance such as VB.NET and Java.

Background

The 'Internal Implementor' (for want of a better name) is a quick trick to reduce the amount of code required to implement the same interface across classes. It consists of an interface definition, a class that implements the basics of the interface and a procedure to using that class in derived and non-derived classes. A derived class in this case is a class that already inherits from some other base class. The normal approach is to inherit the interface and then either implement the interface again in the derived class or wrap an internal implementation instance al la Visual Basic inheritance.

The pattern consists of the Interface class (IMyInterface in our example) and an implementation class (MyInterfaceImpl). The interface consists of two methods: MyBasicMethod is a method that is the same for all class implementations. This is very simple to code in the MyInterfaceImpl class. The second method, MyAbstractMethod, will be implemented differently in each class type. Traditionally, this would be created using the abstract keyword. However, this means that until the method is implemented, classes cannot be instantiated. Therefore, in this case, we don't use the abstract keyword. Instead, we implement the MyAbstractMethod in the MyInterfaceImpl as a method that simply throws an exception, indicating that this method should be overridden.

UML of Interface Implementer

This allows us to use the MyInterfaceImpl class as both a base class (where abstract methods would be valid) and as an internal implementation class (where abstract methods would not be valid).

Example

The example code shows the basic interface:

C#
public interface IMyInterface
{
   /// <summary>
   /// Prints the words 'Interface Implementation' to the console.
   /// </SUMMARY>
   void MyBasicMethod();

   /// <summary>
   /// Prints the name of the implementing class to the console.
   /// </SUMMARY>
   void MyAbstractMethod();
}

The MyInterfaceImpl class implements the basic functionality of this interface, allowing our non-derived class to inherit directly from it, overriding the class-specific method:

C#
public class MyNonDerivedClass : Internal.MyInterfaceImpl
{

   /// <summary>
   /// Override of MyInterfaceImpl.MyAbstractMethod
   /// to provide correct implementation
   /// </SUMMARY>
   public override void MyAbstractMethod()
   {
      Console.WriteLine("MyAbstractMethod called from type {0}",
                                               this.GetType().Name);
   }

}

If the MyInterfaceImpl.MyAbstractMethod was truly an abstract method, this would make no difference. However, to enable the same class to be useable as a contained implementation, the MyInterfaceImpl class uses another technique:

C#
public void MyBasicMethod()
{
   Console.WriteLine("Interface implementation");
}

/// <summary>
/// In derived classes this will write the class name to the console
/// </SUMMARY>
/// <exception cref="System.ApplicationException">
///    Thrown by this implementation.</EXCEPTION>
public virtual void MyAbstractMethod()
{
   throw new System.ApplicationException("Override this method, you fool!");
}

The important point here is that the abstract method is implemented to simply throw an exception. If this was made an abstract method, then you would be unable to instantiate an instance to allow wrapping of this class as a contained implementation, and if the method simply returned without doing anything, the developer might not notice that it had not been overwritten. In this method, an exception would be thrown at runtime if an override is not provided.

The following code shows how the IMyInterface inheritance and wrapping a contained MyInterfaceImpl work in the MyDerivedClass type.

C#
public class MyDerivedClass : SomeBaseClass, IMyInterface
{
   // Internal implementation of IMyInterface
   private Internal.MyInterfaceImpl internalImplementation = null;

   /// <summary>
   /// Default constructor
   /// &;lt;/SUMMARY>
   public MyDerivedClass()
   {
      internalImplementation = new Internal.MyInterfaceImpl();
   }

   #region IMyInterface Members

   public void MyBasicMethod()
   {
      internalImplementation.MyBasicMethod();
   }

   public void MyAbstractMethod()
   {
      Console.WriteLine("MyAbstractMethod called from type {0}",
                                                this.GetType().Name);
   }

   #endregion
}

The class-specific code for MyAbstractMethod is implemented explicitly instead of passing the call off to the contained MyInterfaceImpl class. The BadDerivedClass does this in the code example to demonstrate the exception being thrown:

C#
/// <summary>
/// Bad wrapper for MyInterfaceImpl.MyBasicMethod
/// </SUMMARY>
public void MyAbstractMethod()
{
   // WRONG! Will throw an exception!
   internalImplementation.MyAbstractMethod();
}

This method allows the use of a single class as both an inheritance base and a containment class. Without this technique, two MyInterfaceImpl-style classes would be required: one that provided an abstract method that required overriding, and another that provided a NULL method implementation for containment wrapping. I find this pattern is quick to implement in cases where complex framework classes share subsets of functionality.

Pros and Cons

The benefit of abstract methods is that the compiler catches them if they are not overwritten. This pattern does not have compile-time checking, but uses runtime exceptions to do the same job. This is less convenient in the short term as errors are not detected until testing is performed.

On the other hand, this method is simple and reduces the need for two implementation classes, so in the long run (if you have many cross-cutting functionality subsets, or many classes that must share functionality), then the reduced work more than makes up for the lack of compile-time error support.

Comments Welcome

If you know better than I do about any of this, comments are welcome. There are probably spelling and grammar mistakes in there somewhere, and if you're bored, then feel free to look for them.

Acknowledgements

I would like to thank my employer, John Boother at Autoscribe for allowing me to write this article based on research performed for my job.

History

15th April 2004 -- Ooh, look! My second CodeProject article. People might start to think I know what I'm doing.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Team Leader
United Kingdom United Kingdom
After ten years studying biology and evolution, I threw it all away and became a fully paid up professional programmer. Since 1990 I have done C/C++ Windows 3.1 API, Pascal, Win32, MFC, OOP, C#, etc, etc.

PS. In the picture, I'm the one in blue. On the right.

Comments and Discussions

 
GeneralAttributes Pin
Serg Rogovtsev19-Apr-04 19:58
Serg Rogovtsev19-Apr-04 19:58 
GeneralRe: Attributes Pin
Dr Herbie19-Apr-04 22:00
Dr Herbie19-Apr-04 22:00 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.