Bottom-Up Design and this Thing called an 'Object'

Alan F. Blackwell

December 1993

.EXE Magazine 8(7), pp. 28-32

(pre-publication title: "When an Object is not a Thing")

I was at a loss for words recently when my neighbour on the Tube noticed the logo on a promotional satchel I was carrying. He asked me: what exactly is Object Technology?' What could I say between Leicester Square and Piccadilly? My problem is that object orientation (OO) is starting to invade our speech with some of the worst examples yet of computer jargon. 'Object' is a useful concept in English; not commonly used, but widely understood. It is certainly a more everyday item than jargon terms such as 'entity'. Now the computer industry is appropriating a useful word and giving it a completely new meaning.

It is not only the computer-innocent that are suffering from the redefinition of the word 'object'. Software developers starting to design an OO system and faced with the first blank sheet, often fall into the trap of believing that an 'object' is a 'thing'. This is true in everyday language (unless you are a philosopher), and promoters of OO often imply it in their sales pitches. Be careful, though. For an OO developer, an object is not a thing.

The sales pitch

When vendors are selling object-orientation, they try to describe the benefits in terms that your manager will understand. Lets face it, this means over-simplifying. This is no problem for you, until your project is behind schedule and the sales talk comes back to haunt you. It sounds impressive to a manager, but how would you like to be told, after working on a system for a year, that the analysis should have involved no more than underlining the nouns in the specification, and making each one an object? Or that designing an OO system is as natural as speaking English, because describing the relationship between two things is as good as specifying their interfaces? Or that the coding part of an OO project consists of writing down the behaviour of an object as the analyst describes it?

Claims like this were once more accurate. Object orientation (in the form of the Simula language) was first used for simulation work, where the goals were - to model the behaviour of real world objects. In a simulation program, an object really does represent a thing: in the systems that most of us work on, it usually does not.

When hype fades away

The naive idea that nouns in the specification become the objects in the design can be abandoned as soon as the system boundary is drawn. Most structured design methods start by drawing a system boundary - it is surprising how often people forget that initial step on their first OO project. There is little point in designing an object unless it corresponds to something inside the system, but it is very tempting, for example, to make an object called 'User'. Talk about the user as an object if you like, but remember the user is not inside the system.

As important is the fact that many of the objects in an OO design do not correspond to any thing in the real world. A mouse button click is not a thing; neither is a transaction. What about a print buffer, an event dispatcher or an interaction mode?

In fact when you look at the structure of most completed OO software, you find that the majority of the objects represent concepts in computer science rather than real-world things. As with most advances in programming languages, the ultimate benefit of object orientation is that it helps us to define the behaviour of a system at a higher level of abstraction.

One of the great advances to come with block structured programming languages was the introduction of record data types such as the C struct - these allowed several pieces of data to be treated as a single abstraction. OO extends this concept to include operations on the data as part of the record abstraction. The extension of the record abstraction is often reflected in the language implementation, as in the case of C++, where class is just an extension of struct.

If advances in software come from greater abstraction, then OO designers should concentrate on designing classes that are good abstractions. Some OO languages allow you to treat your abstractions in the same way as the base data types of the language, so that a well designed class can be treated directly as a language element. If you can achieve this, the program code becomes an elegant tool for expressing the function of your system, rather than the usual rats-nest of data and control.

An excuse for having fun

When you look at the most elegant software designs - systems like GNU EMACS or TEX - they succeed because they provide powerful building blocks that can be combined in simple ways by relative novices. The writers of these systems are software geniuses, so we mortals can only aspire to the same elegance. But OO does give us tools that make the job a lot easier. If you can write a decent set of classes, providing building block abstractions for your application, analysis and design will be straightforward. You may have spotted my hidden agenda here. I want to justify what programmers always liked to do best - bottom-up design.

Bottom-up has been frowned on ever since top-down became politically correct in the Structured Seventies. What we all secretly know is that bottom-up is more fun! What half-decent programmer would waste a day working on some functional decomposition user-view data-flow analysis specification when he could be hacking away at code? I don't want to imply that it is a bad thing to think first about what you are doing. Remember that those building blocks have got to be well-designed. That means brainwork, even if it is Monday morning, and you would rather just sit back with your copy of .EXE magazine. What I do want to emphasise is that a good OO design takes the code into account right from the beginning. The non-technical analyst can be even more of a problem than usual on an OO project, because an arbitrary set of functional specs does not necessarily break down into an elegant set of building blocks.

Don't shoot the analyst

The majority of useful classes will fall into one of three basic categories: data objects, user interface components, controllers or building blocks. Data objects are easy to find. Any database record or similar collection of information that will hang around together should become a data object. From the old-style analyst's point of view, these look like the centre of the design. They are easy to spot. A library system, for example, is pretty likely to contain a 'book' object. The data members of this object will include 'author' and 'title'. The operations on the object will include 'withdraw' and 'lost'. It is hard to believe that people get paid for this kind of work, isn't it?

User interface components are easy to identify as well. The simplest are designed as one object per dialog window (or per screen, if you are still building systems for dumb terminals). The data items that appear on a single window are all related to one another, or at least should be, unless the analyst really messed up. It makes sense to design a class for each window.

The more interesting user interface components are groups of fields or widgets that always appear together or behave in a certain way. Consider a customer service system in which there are several different reasons for making a cash transaction. Each of these appears on a different dialog window, but each includes a display field for amount due, an entry field for amount tendered, and another display field for change. These three fields always work the same way and the relationships between them are fixed. The analyst probably won't spot that these can be combined into a single class, but if you code them together they can save you a lot of duplicated effort. They only need to be coded once and debugged once. What's more, when the analyst springs on you the fact that the customer is actually Greek, and all the cash transactions should be displayed in Drachma, you can fix up the user interface with a single change.

Controller objects are the difficult part. These are the objects that only live inside the program and never show themselves to the outside world. You have to be a real programmer to design the architecture of the system at this level. If the analysts don't know what they are doing, don't let them near controllers. A truly bad analyst won't even notice that controllers are needed - they will just draw a bubble called 'system', or a line labelled 'operate', to represent something that you know will take 70% of the effort on the project.

You can recognise a controller object by the fact that it is transient. If the system user doesn't see it and it is not stored in the database, it is probably a controller. Things like transaction queues should be implemented as controller classes; real time or embedded systems tend to have a lot more of this kind of thing. The average data processing application (if you are lucky) is composed mostly of data objects and user interface objects, but if the system is doing something clever, you will probably have to get your hands dirty with controller objects.

Abstractions

Good abstractions are the building blocks of OO design. The more of these basic building blocks you have, the more you can get the real benefits of OO. Unfortunately, there is no quick and easy way to learn how to design a good set of building blocks. You'll learn to recognise them more easily with experience.

I can offer some advice for spotting when you haven't got good building blocks. Look out for anywhere that basic language types are being used for parameters or variables - if the same operations are being repeated on those values in different places, they should possibly be turned into a class. If there are several basic values that are always used together, they should almost certainly be a class.

Consider filenames in C++ as a simple example. All the system calls expect a filename to be a char*, and C programmers are used to thinking of a filename as being a char*. You can store them in this form and pass them around the system in the same way. Think how convenient it would be, though, if you never had to write code to check for valid construction, or to concatenate to paths, or to allow for portability between UNIX and MS-DOS format path separators. If you define a filename class, it might still contain the same char* data, but you can build in checks to make sure that there are no spaces in the middle, and that the last character is not a path separator. Making system calls can be just as easy as it was with a char*, because you can define a type conversion operator that passes the char* to the system call.

Even better, you can build in the system calls as operators on the filename class itself. The type checking of the compiler now starts to work for you (rather than against you, as it normally seems to); you will not mix up filenames with other string data such as user names. You can distinguish in advance between a directory path and a file path. Perhaps 'directory' should inherit from 'filename'? You can start to define operators on these classes to do useful things. What would you expect to happen when you assign a filename to a directory? Should you get the directory path? How about adding a string to a directory? Maybe that should concatenate them and return a filename?

As you can see, defining building blocks opens up a whole spectrum of possibilities at the bottom levels of your design. You can see some good examples of building block design by reading through the source code for class libraries. Eventually, we might all have access to class libraries that do everything we want at this level. In the meantime, we have to design many of our own building block classes and good abstractions, like the filename class, which can simplify the design of a whole system. If you are careful you can use a lot of the same building blocks in your next project and get some real improvements in productivity.

The bottom-up manifesto

In this article I have argued for more bottom-up design; the main benefits of using an OO language come only when you have a working set of building blocks. This means that a good programmer using an OO language can work better, even using old-style analysis methods: the building blocks should be designed as a separate exercise from the analysis. Don't confuse what I am saying with OO analysis. That is a completely different field, which can be used either in conjunction with an OO language, or with a conventional language. OO analysis is not much like programming, but you may find it entertaining

What you need to avoid are the people who think they are OO analysts, but who do not really understand OO programming. They are rather too common. If you aren't careful, analysis objects can pose as software objects and take over your design - like the killer vegetables in Invasion of the Body Snatchers. If you are under attack from analysis objects, just quote the design creed of the bottom-up manifesto: 'a class is an abstraction; an object is not a thing.'


Click to return to Alan Blackwell's home page.