The two people I always have in mind when designing a new class are:
- the user (if it is a visual component)
- the developer who is going to be interacting with it.
I’m a firm believer in user-driven design, and will tend to structure my classes in order that they make most sense to how they will be organised on-screen, if they are visual components. However equally important to this is designing classes and their interfaces with other modules in such a way that they are easy to interact with from other modules. This is paramount to a well-structured application.
As a Cocoa developer the place where I find this philosophy is most important is the exchange of data from Model to View, passing through a Controller — the mediator in the MVC pattern. As a developer who often designs and builds data-intensive views, building something which not only works well for the user but also the developer is important to structuring classes which have clean interfaces and exchange of information between one another.
The bigger picture
In an object-oriented context, my first thought with all of my classes is how am I or someone else going to use this thing? You could have the best implementation in the world but a poorly thought-out public interface for other classes is likely to lead to problems down the line. This task comes before I even start to implement anything internally in the class.
The main benefit to this is that by designing the interface before you do any actual coding of inner workings, it gives you a broader sense of the class and will allow you to make changes as early as possible, potentially saving you a lot of time.
This is supported by Hunt and Thomas in The Pragmatic Programmer, in the section on Prototyping Architecture:
Most prototypes are constructed to model the entire system under consideration. As opposed to tracer bullets, none of the modules in the prototype system need to be particularly functional. In fact, you may not even need to code in order to prototype architecture—you can prototype on a whiteboard, with Post-it notes or index cards. What you are looking for is how the system hangs together as a whole, again deferring details.
Of course internal details are important and can affect how the class will be used, but designing the interface around the implementation is rather backward, because the internal implementation has little dependency on anything (aside from some fundamentals such as the storage and structure of data, for instance). The public interface, however is important to get right as a bad one can make other parts of your application inefficient or poorly constructed.
Development process
1. Design the public interface by listing methods
I usually start out by creating a list of public methods which other classes will call; be that processes or querying or setting state.
A lot of Cocoa classes use the delegation pattern. That is, the class will query its delegate (a Controller in the MVC pattern) for data to such as information to display in a table, or to notify it that something of importance has happened. At this stage I will also write a list of methods that will be called upon the delegate, to get a feel for how the interchange of information will work.
Of course at this time you may not know the full picture of how this class is going to work, but build upon what you know already. Doing it to the best of your knowledge at the time is what you are trying to do, rather than going in completely blind when building the control.
2. Test how other classes will interact with your new class
Next, I will actually call these methods that I have defined on my new class in another module, or implement delegate methods in my class’s delegate, using a test Model if they return any data.
This is invaluable when starting out, because it allows me to get a feel of how I will interact with this class when I am finished with it; at that point the internal implementation will be irrelevant from the point of view of any external classes, and this is an opportunity to revise the public interface of the control before getting bogged down by the internal implementation.
This might seem a somewhat back-to-front approach; instead of implementing the internal workings of a control, then creating the interface for other classes to interact with it, I will implement the public interface with no internal backing, then implement the internal parts later.
However, this approach has several advantages:
- You get a better sense of the overall design of the control. Before you start coding anything internal, you get a much broader picture of how the class is going to operate, which can be useful when it comes to implementing the internal workings later.
- It makes you think about how other classes/modules will use it. Doing these steps first allows you to think about how you (or another developer) will want to use the class, irrespective of how it will function internally. For another developer who may not understand the exact inner-workings of your class, making this abstracted to suit the needs of other classes will make using it much easier.
- Changes to the interface at this point are very cheap. If you decide you don’t like the public interface at this point, it is very easy to change it with few repercussions. If you design the interface as you go along, any changes you make will often affect the internal structure which can be costly.
3. Revise the interface
If there are any things which I’m not happy with I will now change these with quite a minimal impact, as there will be no underlying changes to the new class. I can then make sure the class I’m testing interactions with is updated to reflect these changes, and I will keep repeating this until I have something I’m initially happy with to start implementing.
4. Internal Implementation
Now I can start to build the internal workings of the class, with this rough guide for how I want it to behave and how it is constructed.
Of course as the implementation continues you may have to alter the public interface as you add features and capabilities to the module, but the initial design you have come up with will likely minimise this, if you have enough of an idea of how the module is going to work.
To Conclude
It may seem that I am suggesting that this is an ideal process, one by which up-front design will create a perfectly designed interface that won’t have to be revised; this is not the case in many places, and as you start to build more complexity or requirements change it is certainly possible that you may have to gut your class.
Real software development is anything but perfect and ideological, but by thinking and revising module design and architecture up-front, it will help to reduce throwaway code that you write, and on the whole save you time.
Comments — 2
Jan 18, 2011
That’s how test-driven development works, too. You implement a class by defining how you want to use it, then writing the code that lets you use it that way.
Jan 18, 2011
@iamleeg: Oh yes, of course! That’s another good way of looking at it.
Add comment