Use interfaces to protect against change
This is one of the more challenging areas of programming. It doesn't really involve doing much extra work at the time but the correct design decision can make your life so much easier in the future. It reminds me of one of the more memorable saying's
"closed for change but open for extension"
It is known (having just looked it up again) as the open closed principle and there are many articles linking to it but I first read about it here
and I found the objectmentor site a brilliant resource for OO topics and well worth checking out if you are interested in that kind of thing.
This saying encapsulates what I try and do. I want to write code that won't have to be changed in the future but if at a later point we need to add some changes in like a different algorithm or add a new report then you should be able to extend the current code and add in a new report. That's what happens in a perfect world.
To achieve this result you need to think about what areas which are likely to change and in these areas you need modularise the code so it is in small components. This means that you will be able to reuse these code components. You also need Design an abstract interface to your code. I often call this base classes and these can be abstract classes or interfaces but the purpose of the class is hide the change behind the class and reduce any coupling to the classes behind it.
An example of this would be if you were working on a piece of software which does reporting. You currently support report formats of html. You then want to add a pdf report. If you planned ahead you could have a put in place reportFormat interface. The rest of the code would still interact with the reportFormat. So the reportFormat interface means the only changes will be behind the reportFormat interface and the other classes outside of the reportFormat package won't know that a change has taken place.
So the interface is used not only to hide the information of what report format classes implement it but it also hides the changes from other areas of code that use it. We have also haven't changed any of the code that was already there.
Obviously this is a simple example using an interface where you think the code would change in the future is going to make it a lot easier to maintain and extend this code.
I found I had difficultly writing this type of code earlier in my programming days and it was because I wasn't thinking about information hiding and loose coupling because I didn't know about them, all I was thinking about was writing the code to do what the spec wanted it to do. I didn't have any code reviews either so no one really told me that their was a better way until I found out for myself. Writing the code to do what the spec says is half of the solution, keeping one eye on the future so you will easily be able to maintain and extend the code is the other half of the solution.
3 Comments:
This is a very good question and I often found that I use to use abstract base classes a lot more often.
I then started running into problems trying to maintain the abstract classes and found that interfaces seemed to get rid of this problem.
I wrote this blog entry which dicusses interfaces and abstract classes, although I don't believe they are in competition with each other it's an interesting dicussion to compare them to each other
http://hoskinator.blogspot.com/2006/07/why-interfaces-are-better-than.html
back to the question. I think they key to using interfaces is to make sure the other code is small modular code and allow the common code to be used by a few of the classes implementing the interface. You should always try not to repeat the same code in different places (keep it dry (don't repeat yourself)) This way if the code is the same its used and if it's different you create another method/class.
I have blogged about similar stuff on my blog, so maybe type in interfaces, abstract classes and see what else pops up.
I would recommend reading my this blog entry to help write modular code
http://hoskinator.blogspot.com/2006/06/10-tips-on-writing-reusable-code.html
By The Hosk, at Mon Sept 11, 04:10:00 pm 2006
Picking at a side issue...
I'm amused at the way that Robert C. Martin managed to completely turn around the "Open-Closed Principle" from what Bertrand Meyer wrote.
Meyer's OCP is basically this: once a class is written, only bug-fixes are made to the code. If you need a class with new or changed features, you must do it by deriving a new subclass. The public interface of the new subclass oftentimes is substitutable for the base class, but it doesn't need to be (although languages like Java don't allow for this possibility).
Martin's OCP is basically this: once a class is written, you can change the implementation however you want, as long as the new public interface is substitutable for the old.
Meyer closes the implementation and opens the interface; Martin opens the implementation and closes the interface.
These are two very different viewpoints, and Martin doesn't even realize it.
By Doug, at Mon Sept 11, 09:29:00 pm 2006
If you end up with duplicated code when you are using interfaces, try to think about another way of solving it instead of using an abstract class.
If after reading this post, you can't see a way to remove duplication without using an abstract class, feel free to email me with something compilable - ricky.clarkson@gmail.com - and maybe I can.
The most obvious one is delegation - put the common code in a utility class and call out to that.
void doSomething()
{
SomethingDoerUtility.doAThing(this);
}
Of course, if you find yourself doing this, then maybe doSomething really belongs in the utility class anyway - does it vary between instances?
Another approach is to maintain a Runnable that does the thing that you want, then you can swap the Runnable out for something else.
public Runnable getMeAThingThatDoesSomething()
If you might want the Runnable to return something, or take something as a parameter, then you might want to write a Function type.
interface Function
{
Object run(Object input);
}
Generified (I've used [] instead of angle brackets to avoid messing around with HTML escaping):
interface Function[T,R]
{
R run(T input);
}
I already have this and some others, in functionalpeas - http://code.google.com/p/functionalpeas/
On a side note, I think you should avoid using Void as a type parameter (the only valid value is null), and choose a different interface. For example, when using Function, I found myself not caring about the return value, so making Function[Something,Void], which results in an ugly 'return null' in the Function implementations.
For this reason I made an:
interface SideEffect[T]
{
void run(T input);
}
and a badly named:
interface Constant[R]
{
R eval();
}
The latter hasn't made it into functionalpeas yet, and it might not.
By Ricky Clarkson, at Tue Sept 12, 01:00:00 pm 2006
Post a Comment
<< Home