I was in a situation with the project I’ve recently joined that I needed a subclass to have different behaviour from one of its dependencies. The dependency was directly coupled to the class, not only through being directly instantiated by the class (not what I am trying to solve here, but definitely on my hit-list) but also being held as a reference to that particular type. Here’s a (very simplified) example:
class SomeServiceClass { public void ProvideService() { Console.WriteLine("Default Service behaviour"); } } class SomeBaseClass { public SomeBaseClass() { m_service = new SomeServiceClass(); } protected SomeServiceClass m_service; public void DoSomething() { m_service.ProvideService(); } } class SomeDerivedClass : SomeBaseClass { }
NOTE: Exposing the m_service
field to sub-classes is not something I consider to be good practice. There are obviously further problems with this code that I am NOT addressing here.
I needed SomeServiceClass
to behave differently when invoked from SomeDerivedClass
. The service was sufficiently complex (read: violated the Single Responsibility Principle big-time) that subclassing it didn’t particularly appeal. Introducing an Interface would allow me to alter the service entirely for SomeDerivedClass
whilst leaving SomeBaseClass
and all its other sub-classes (oh yeah, did I mention it was only one of many sub-classes that needed this altered behaviour?) with the existing code.
First off, define the interface. As this is being derived from an existing class and the idea is to make as little change to existing code as possible, the interface should end up looking exactly the same as the public signature of the class:
interface ISomeService { void ProvideService(); }
This allows SomeBaseClass
to redefine the m_service
field as type ISomeService
and the DoSomething()
method is none the wiser. However, the constructor explicitly creates a SomeServiceClass
instance to assign to it. Since we made the interface the same, we could just make SomeServiceClass
implement ISomeService
, but it would be nice if we could leave that class unchanged. Instead, it would be great to present an actual SomeServiceClass
instance looking like an ISomeService
. This can be done with the Adapter Pattern. We can make a new class that implements ISomeService
and delegates all calls to an underlying SomeServiceClass
instance. It would look something like this:
class SomeServiceAdapter : ISomeService { public SomeServiceAdapter(SomeServiceClass baseServiceImplementation) { m_baseServiceImplementation = baseServiceImplementation; } private SomeServiceClass m_baseServiceImplementation; #region ISomeService Members public void ProvideService() { m_baseServiceImplementation.ProvideService(); } #endregion }
This allows us to declare and initialise the m_service
field as follows:
class SomeBaseClass { public SomeBaseClass() { m_service = new SomeServiceAdapter(new SomeServiceClass()); } protected ISomeService m_service; public void DoSomething() { m_service.ProvideService(); } }
Now that our consuming class is referencing an interface, we can supply a different implementation for the derived class. As I mentioned above, I would much rather see the implementation passed in from outside this class, but not every architectural weakness can fixed at once, so we’ll work with what we’ve got for now, and we have got is a protected field that the derived class can access. So, instead of using the instance that the base class initialises it to, the derived class can override it in its constructor. But first, of course, we need to have an alternate implementation to override it with:
class SomeServiceAlternative : ISomeService { #region ISomeService Members public void ProvideService() { Console.WriteLine("Alternative Service behaviour"); } #endregion }
Our derived class can then do this:
class SomeDerivedClass : SomeBaseClass { public SomeDerivedClass() { m_service = new SomeServiceAlternative(); } }
Again, I’d like to stress that Dependency Injection would be the way to go if you have the choice. In this instance, I had a specific sub-class that wanted different behaviour, and as such I was able to get the sub-class to define its preference. We are now additionally coupled to the not just the original service class (still being new-ed up in the base class constructor) but also the new interface and its two implementations. However, I took the view that the dependencies on the classes being confined to just the constructor was a better state than dependening on the service class throughout the base class. Injecting these classes in from the outside would reduce the dependencies to just the interface.