And thou shalt set bounds unto the people round about saying 'Take heed to yourselves, that ye go not up into the mount or touch the border of it:' whosoever toucheth the mount shall surely be put to death: there shall not an hand touch it, but he shall surely be stoned, or shot through; whether it be beast or man it shall not live : when the trumpet soundeth long, they shall come up to the mount. Ex 19:12-13
I suppose most people have heard of Moses and the ten commandments. What most people don't realise is that Exodus & Leviticus give us one of the earliest and most sophisticated examples of object oriented interfacing available in print! The particular example is a parent object with many children. The parent is a self-contained (indeed self-defining!) entity, the children are dependant objects derived from the parent (Ge 1:26). For various reasons we can't go into the children have become largely autonomous and have overridden or changed most of the properties and methods of the parent (Is 55:8) whilst the parent remains static (Mal 3:6).
Now in the middle of all this it becomes necessary to pass some data and methods (the commandments) from parent to child. The problem is the gulf between the objects is too large for data simply to 'be there'. A link or communication medium is required. So God speaks. But, the gulf between the objects is too large (the sinful population could not freely mix with a sin-hating God). A barrier is required to prevent the people from harm and to prevent polution. So boundaries are placed. So with transport layer (Moses) and communication medium (tablets of stone) in place the transaction was performed. Then as we go through the remainder of Ex & Lev we find a protocol established to allow data flow and free movement allowed and encouraged, but always within boundaries. Sometimes these boundaries were transgressed, usually with fatal consequences (eg Lev 10:1-2)
In previous articles I have drawn out the reasons for interfaces. On the one hand you want code segregation so that bugs and mis-designs are confined and do not pollute the whole project. On the other hand you want objects that are useable and provide information to the outside work.
At first glance these criteria appear contradictory and most people try to re-define or re-work one of the two requirements to achieve harmony. This is a mistake. The two requirements appear contradictory for the simple reason they are. It all becomes clear if you think of it in terms of information.
|Information type||Let information flow||Don't let information flow|
|File structures||Everyone can access data directly||Data structures can be changed without code re-write|
|'Temporary data structures' used to store information||Everyone can get at all information in an object||A different strategy can be employed without a re-write|
|Minor 'service' procedures and routines||Every ounce of code in the system can be re-used||Code within an object can be deleted when no-longer used. Service routines can change specification.|
|Global variables||Object can be integrated fully with parent application without 'strange' restrictions||Code can be re-used|
|Module / class variables||Everyone can 'peek and poke' the variable to fine-tune the code behaviour||Procedures can be maintained without breaking existing code. Avoidance of 'strange variable corruption's' across procedure calls|
|Optimisation 'tips and tricks'||All objects used 'well'||Objects can change footprint as time progresses|
|'How this method works' documentation||Easy to use object quickly with reasonable understanding.||Methods can be maintained without invalidating documentation|
I have deliberately only noted the good points to show just what a cleft stick the interface writer is in. If you just skim down the middle column you will rapidly convince yourself that is what you want from a RAD system. Everything is easy and everything is there and you can twiddle to your hearts content. Now if you skim down the right hand column you will find everything you want from a system that someone else has written, maintainability, stability, reuse and leverage.
Now look at the negation of the above table
|Information type||Let information flow||Don't let information flow|
|File structures||File structures are locked unless all code under your control and you don't mind massive edits||Data must be got at through functions or references which is slower|
|'Temporary data structures' used to store information||The first cut and temporary data structures become fixed. If a new idea comes along tough.||It is possible that information will be computed internally that is not available outside.|
|Minor 'service' procedures and routines||Complete code bloat, once a routine exists it has to for all time, you cannot change the spec.||Some interesting looking algorithms cannot be directly leveraged from outside|
|Global variables||No code reuse||Object can only interact with parent application in tightly defined ways|
|Module / class variables||First cut at algorithms becomes fixed. Sometimes you cannot even fix bugs without breaking things.||You can only modify behaviour the programmer predicted (unless you derive)|
|Optimisation 'tips and tricks'||Causes code to be written in 'odd' ways which become pathogenically bad if the object changes||Objects can be mis-used resulting in a 'sluggish' system|
|'How this method works' documentation||Either maintenance becomes impossible or documentation becomes erroneous||You have to dig if you want to understand how something works|
Now if you scan either column you will decide you cannot really face either set of draw-backs.
So the conclusion to draw is that if you produce an interface you can guarantee that you aren't giving people all the functionality they want and that you are going to give them a set of problems they don't want. So what kind of interface to you produce? Where do you draw the line? In the discussions that follow I will adopt the convention that an object (and hence an object writer) is trying to be as secretive as possible whilst and object user is trying to be as invasive as possible.
Faced with such extreme problems most of us will instinctively aim for a middle of the road solution. Allow some information to flow and not other bits. However this policy does have some drawbacks.
In other words going down the middle of the road gives you a chance to get hit by a truck from both sides ...
Let us bring the discussion of interfaces into the context of the essays I have written so far. The conclusions I have reached are (essentially)
The astute will notice a common thread in the above five items, they all suggest the pain comes 'down the line'. In other words, it should always be worth taking a hit 'up front' if it will pay us back (with interest) later on.
Now go back and scan the two tables, you will notice an odd characteristic, letting information flow helps you now, preventing it flowing (encapsulating) helps you down the line.
This leads me to the direct conclusion that the interface you should produce is whatever one suits you best. Let me expand that a little
An object interface should contain any information and facilities that the object writer so chooses provided that he undertakes to implement them fully and support them totally for all time.
That is (of course) a technological statement, commercially the object writers boss will have some input and someone else may end up maintaining but the principle is the same.
If you take this on board it has startling repercussions. The interface is just a list of 'goods offered', it has nothing (logically) to do with the underlying structure of the code (the implementation).
If you like the interface becomes the 'ten commandments', written on tablets of stone, inviolable and non-negotiable for all time.
In some ways this approach makes the object writers life easier, he now has complete freedom of movement behind the interface. It also makes the object users life easier, he can easily and reliably go to the object to perform a function, provided it has the function they want. In other ways the object writer now has to work even harder than before. Not only does the interface need to be written but it must be absolutely clear what is being offered.
Let me define a small interface for you and talk you through it :
BOOL EQUATE(BYTE) ! 0 is false, all other values true
Yada yada !inside a browse class somewhere
! This flag is checked upon window reset
PinkButtons BOOL ! 1 ==> button text bright pink
A nice, contained easy to read interface? No, actually, a complete mess.
So what do you do? I have two major complaints with the above, encapsulation leakage through comments and the ambiguity of having a property 'float'. I solve the first with elimination, the second with convention.
Rule 1: A comment is part of the interface so only write it if you mean it and intend to go on meaning it.
Rule 2 : Do everything the simplest way possible, so if you are doing something complex people know there is a reason.
Rule 3 : Don't fake it. If you can encapsulate do, if you can't don't pretend you are.
I now have two ways of defining the above :
PinkButtons BYTE ! 1 ==> button text(s) default pink
SetPinkButtons PROCEDURE(BYTE) ! 1 ==> make button text(s) default pink
The first implies a read-write property that defines the state of the text of the buttons. It can be treated as if the object were using it (which it may be) and the object undertakes to look at that flag 'when appropriate'. But when? Don't know, not part of the interface.
The second is clearly a write only procedure that will turn the buttons pink. Because I have used a function rather than a variable rule #2 would suggest I'm up to something. Although the interface doesn't actually define it you can probably assume the action happens directly. The second could have a GetPinkButtons too.
Note that most of the above 10 points have been covered.
Of course this style has down sides too. The biggest is you need to learn the conventions. The next is that sometimes the interface keeps you from things you want.
The solution to the first of these is documentation. Now documentors have to be very careful. Without care they become the biggest encapsulation leakage going and as such a complete liability. Of course without documentation you end up with a nice, well defined object that is not used by anyone. The policy we adopted in the C4 application handbook was schizophrenia. Each part of the interface is documented twice. First is the 'official' documentation, then under the heading 'implementation' comes all the dirt. Hopefully this enables us to enjoy the best of both worlds.
The solution to the second is 'tough'. More specifically you simply acknowledge the down side. OOP and encapsulation equals restriction and learning curve. Of course the real solution is you make up the gap. The restrictive interfaces makes the object user work slower, but the counter is you make them work less. And that is precisely what encapsulation offers.
Firstly the extra space you have inside the privacy of the object allows you to do to town on the features, therefore the object user works less because there is less to do. They also maintain less because the code is much safer.
But the real knockout blow comes with code re-use. Let us suppose that 10% of the code you write can be used, in the average application you produce, with only 1% of the original effort. That means in 5 years time you can deliver an app, in less than 1 week, that looks as if it took 6 months to write. And those figures (especially the first) are very pessimistic.
Code re-use will be the subject of a later article.