Plug into the Wall: Interfaces to the Outside World
Just to be clear, this article isn’t about interfacing with hardware, though what it says does apply a little bit.
You’ve Heard It Before
You’ve probably heard it quite a bit, actually: Program to an interface, not an implementation. More than likely, though, you’re not doing it nearly as well as you think you are.
When you use a library in your project, are you using its interfaces as your interfaces? Or did you design your own interface to work with the library in a way that you want to use it? If not, you won’t be able to switch to a new library that does the functionality better. If not, you could be stuck with chunks of code revolving around pleasing the library being intermixed with your logical code.
Is there a layer of abstraction between your code and the framework you’re using? If not, you cannot change to a different framework without a lot of work. Even directly connecting with a framework is an implementation detail that should be abstracted away.
This is the idea that MVC was originally based on. It placed interfaces between each of the three layers in order to make it easy to swap views and models out (technically, you could swap out a controller too, but the controller is typically the mediator and sticks around).
This basic idea is brought up in a couple spots in Clean Code, which is generally regarded as the authority on good code. The most memorable being the first case study in the next section.
Case Studies
API Doesn’t Exist Yet
In chapter 8 of Clean Code, there is a section called Using Code That Does Not Yet Exist. In it, Uncle Bob describes a scenario where he and his colleagues were writing software for a radio communications system. In the system, there was a subsystem, Transmitter, that wasn’t defined yet, and these developers didn’t want to stop working just to wait for that to be defined.
What did they do? They worked around it. Whenever they ran into code that would require using the Transmitter, they simply thought about how they thought it should be used. After a while, they figured out a good interface that they wanted to use and made a fake transmitter that used that interface.
Once the real transmitter API came in, they simply wrapped it with an adapter (ss in the Adapter Pattern) to their own interface, making it so the only changes that were needed in the older code was the code that created the fake transmitter and replace it with code that made the adapter to the real thing.
Fitnesse MVC
Bob Martin also gave a talk once on the how the MVC pattern is supposed to supply interfaces for the interaction between the layers, where he gave an example of when his team was working on Fitnesse.
They initially started off saying that they’d need a MySQL database, but someone piped up, saying that they needn’t make their database decision yet. So they didn’t. Instead, they created their model interface and simply had it persist in memory at first. They didn’t need persistence between runs while they were building it.
Later on, they did start to need it to truly persist. Again, they almost made a database decision when someone said that they should just do flat files to put off the db decision longer. So they wrote up a version of the model that stored itself in files instead of the database, while keeping the same interface that they had before.
In fact, when they were done with everything else, they decided that they didn’t even need database support, so they shipped it without it. The only reason they eventually added it was because a client of Fitnesse required the use of a MySQL database. So they very quickly and easily added the support.
What Should I Do?
Any time that you’re working with code that you don’t have control over, you should separated it from your real code by making an Adapter or Facade for it. This interface should be defined the way that you want to use it.
MVC Frameworks provide an interesting challenge. The one big piece of advice that I have for you is to not use their controllers as your controllers, especially if their controllers use annotations and/or framework-specific types. Those controllers should simply act as the view’s adapter to your own controllers, so their use is to transform what the framework gives them into something that can be delegated to your controllers.
You should make adapters and facades for libraries that you use, too. Even if a library has an interface that you really like, you should make your own, even if it’s an exact duplicate (you can remove parts of the API you never use, too!). Then, if you end up swapping out the library, or the library makes a breaking update, you can use the new library with the old interface by updating or making a new wrapper.
The Benefits
The first benefit of defining your own boundary interfaces is that they are under your control. This means that you can change them however you want in order to make the code that uses them cleaner and/or more efficient.
It also provides a good, practical place for your helper methods for working with certain libraries. Instead of writing helper classes full of static methods, you can put the functionality into the wrappers, whether it’s explicit helpers that are a part of the interface, or it’s implicit helpers that are implemented privately by the specific wrapper. Either way, it removes the need of a barely-helpful helper class and puts the functionality in a much more helpful place.
Using your own interface also allows you to not have the component ready when you start working with it. This can be due to waiting for the implementation to be ready or due to you putting off the decision of which component to use until later.
It makes the user code cleaner (assuming you use a clean interface), which you can clean up and refactor at will because you’re in control of the interface it’s calling.
It provides a single place for change. Need I say more?
Exceptions
Most rules have exceptions, and this rule isn’t an exception to that. One exception to this rule, though it doesn’t have to be, is with your testing framework. There is a chance that you’ll change your testing framework, but you probably won’t. If you expect you will, try to extract what will be common to be called from all frameworks and put that somewhere separate. Then have the code that actually follows the framework call that extracted code.
If there is a framework or tool that your company always uses, you should be able to safely assume that it won’t be switched out. Keep in mind, though, the benefit of creating your own interface for the sake of cleaner code. This is still a valid reason to create your own.
Oops! We WEREN’T the Exception
If you designed your application using a certain framework or library and you suddenly need to switch out to something else, or the framework or library made a breaking update (but you NEED the new functionality that comes with the update), your first steps should be refactoring the code to use a new interface (if you make one that looks JUST like the tool’s interface, it’ll make the transition even easier!), in order to protect against it happening again. Often, this will not only protect you later, but it will actually make the current transition to the new software easier.
Time to Confess
I must confess, I haven’t done hardly any of this. I’ve heard about it a couple times, but it slipped from my mind before I had a chance to apply it. I’m making a resolution to start doing this a lot more, and I’ll probably tell you guys some day about how it’s working out for me. I expect that it’ll make for some nicer code in many places, but it’ll be tedious work to get it set up and started.
Reference: | Plug into the Wall: Interfaces to the Outside World from our JCG partner Jacob Zimmerman at the Programming Ideas With Jake blog. |