Wednesday, February 15, 2012

Header Interfaces or Inverted Interfaces?


Thanks to Derick Bailey for introducing me to this concept: Header Interfaces.  A Header Interface is an interface of the same name as the class which exposes all of the class's methods, generally not intended to be implemented by any other classes, and usually introduced just for the purposes of testing.  Derick compared these to header files in C++. In my earlier Interfaces, DI, and IoC are the Devil post, these are really the kinds of interfaces I was rallying against.  (I'll use the ATM example from Bob Martin's ISP article throughout this post)

public class AtmUI : IAtmUI {...}

One of the things I really found interesting about this was the idea that there are different kinds of interfaces.  So if Header Interfaces are one kind, what other kinds are there?

The first things that came to my mind were the Interface Segregation Principle and the Dependency Inversion Principle.  ISP obviously deals directly with interfaces.  It basically says that interfaces should remain small.  "Small" is always a tricky word, in this case it's about making sure that the clients consuming the interfaces actually use all the methods of the interface.  The idea being that if the client does not use all the methods, then you're forcing the client to depend on methods it actually has no use for.  Apparently this is supposed to make things more brittle.

I said "apparently" because I've never directly felt this pain.  I guess it really only comes into play if you have separate compilable components and you are trying to reduce how many things have to be recompiled when you make a change.  I use .NET, so Visual Studio, and it's not a picnic for controlling compilable components...  I think I have this requirement though, and just haven't figured out how to deal with it in .NET.  But for the moment, lets assume we agree that ISP is a good thing.

public class AtmUI : IDepositUI, IWithdrawalUI, ITransferUI {...}

The DIP leads to a similar place as ISP.  It tells us that higher level components should not depend on lower level components.  There is some room for uncertainty here around which components are higher than others.  For example, is the AtmUI a higher level than the Transaction?  I'll go with no, because the Transaction is the actual driver of the application, the UI is just one of it's collaborators.  Because of this, the DIP leads us to create separate interfaces to be consumed by each Transaction:

public class AtmUI : IDepositUI, IWithdrawalUI, ITransferUI {...}

So, maybe there are at least two types of interfaces: Header Interfaces, and what I'll coin Inverted Interfaces.  In the last post I talked about the "Service Layer" pattern.  It generally leads to the creation of what feel more like Header Interfaces.  But this is tricky, because the only difference I can really find here is based on who owns the interface.  An Inverted Interface is owned by the class that consumes the interface, and a Header Interface is owned by the class that implements the interface.

But sometimes the difference isn't really that clear cut.  If you're TDDing your way through an application  top-down in the GOOS style, the Service Layers are designed and created based on the needs of the "higher" level components.  So the component, and it's interface, both spring into existence at the same time.  So if the service only has one consumer right now, the interface feels very Header-y.  On the other hand, it was created to fulfill the need of a higher level component; very Inverted-y.

But if someone else comes around and consumes the same service later: well now we have some thinking to do. If we reuse the interface, then I guess we've made it a Header Interface.  Would Uncle Bob have said to create a new interface but make the existing service implement it?  The lines are blurred because the components we're dealing with all reside within the same "package" and at least right now don't have any clear call to be reused outside this package.

Sadly, the introduction of these interfaces brings us back to Dependency Injection.  So in the next post, I'll look at the Dependency Inversion Principle, and the consequences of these Inverted Interfaces.

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.