Reusable code is the holy grail of software development. -- Robert Biddle and Ewan Tempero
The idea of software reuse has hugely influenced the thinking of the OOD community. Many OOD principles were first developed as an aid to reuse.
Reuse is now unfashionable, because it has proven harder to achieve than everybody expected. But the principles still matter. In fact, some people now argue that designing reusable code is just the same as designing good code.
Martin Fowler 1997 (p. 11) gives a simple overview:
- "If the average professional is asked what the principal benefit of object technology is, the answer is almost always reuse. The vision is that of developers being able to assemble systems from tried and tested off-the-shelf components. Many of these visions have been very slow to appear. In some cases reuse is beginning to show, most notably in GUI development and database interaction. Where they are not appearing is at the business level."
Johnson and Foote 1988 is a very important early paper on OO. Among other things, it provides insight into the reuse mindset.
Granularity of reuse
Arthur Riel 1996, p. 8, makes an interesting observation about reuse. He says reuse is supported by OO (in part) because classes have the right granularity: somewhere between statements/functions and applications:
- "The term software reuse has become a major buzzword in the object-oriented community. Why all the excitement with reusing software in the object-oriented world? We have always had software reuse. How many developers have used an `if` statement in their favorite programming language? How many C programmers have used `printf()`? How many Pascal programmers have used `writeln()`? Why the renewed discussion on software reusability? As it turns out, there is an inverse proportion between the size (or granularity) of the code we reuse and its flexibility. `If` statements are very small and very flexible, while Lotus 1-2-3 [a spreadsheet] is a much larger level of reuse, but it has a specific purpose. It is thought that the types of constructs found in the object-oriented paradigm find a happy medium between the granularity of the software and its flexibility."
He goes on to note that the theory has not delivered as much as hoped:
- "Unfortunately, the level of software reuse achieved in object-oriented applications has been disappointing for many developers."
Types of Reuse
- Opportunistic: When beginning a new project we realise that there are some components which we can reuse
- Internal: When we reuse our own code. This may be starting from a code base, or maintaining and controlling a single component that is shared between several projects.
- External: When we decide to purchase a third-party component. This can be very cheap at first, but we must consider how long it will take to find, learn and integrate these components.
- Planned: When we strategically design components to be reusable.
Examples of Reuse
- Libraries: are used to avoid "re-inventing the wheel". They typically provide a set of common operations which are needed for several programs. They are typically treated as a "black box" which has disadvantages such as reduced flexibility. Libraries are typically very well tested and cover a very wide range of unusual or arcane cases.
- Frameworks: are large pieces of software often provided by a third-party. They differ from libraries in that they are usually only applicable to a family of domains.
- Design Patterns: are used to develop a solution to a recurring type of problem. They are a more conceptual form of reuse, and so are more flexible. They aim to provide a reusable solution to a problem, as opposed to reusable software per se.
Why reuse doesn't work
As stated above, software reuse is a good idea in principle, but it doesn't always work in practice. Typically what happens is a developer is faced with a problem which appears generic enough that they decide to develop a reusable library in case the same problem appears in a future project. Developing a generic library takes more effort than coding for the specific problem at hand. When dealing with an actual customer, it can be difficult to convince the customer that this is a good idea, since they just want their software to work. The first problem is therefore: The developer usually needs to take some of the initial development effort on their own back.
The second problem is trying to design an interface that can be reused by a project which doesn't exist yet. It is often hard enough to develop interfaces when the requirements for a project are known (if constantly changing), so developing a library to be generic enough for future applications can be very difficult.
The third problem is: no matter how much effort you put into solving problem two, it just never seems to work. A new project comes in with a similar problem to one that you have written a generic library for. But it is just slightly different. It is often things you never envisaged, such as a new project being multi-threaded, and the whole library now needing to be thread safe. Like problem one, it can be hard to convince a customer that this is their problem. Again, they just want their code to work, they don't care about your generic reusable library. The problem here, is that it is often difficult to tell whether developing two independent solutions for each customer would have required less effort than developing the generic reusable library. Ideally the generic solution will eventually pay itself off after being used in several projects. However, this can be a gamble that your library will be useful for future projects.
After a library has been reused a couple of times for a few different projects, and tweaked and hacked by several developers for the quirks of each new project, it starts to get bloated. Often you end up with problems such as the same functionality being implemented multiple times. This can happen because two different projects need slightly different versions of the same function, or because a developer didn't know the library already supported a feature and added it again with a different name. A side effect of this problem, is that an earlier customer may request an upgrade of the library, possibly for a bug fix or new feature. You checkout the latest version, with all the tweaks and hacks for all the various customers only to discover that it no longer builds, or worse, it does build but doesn't work correctly, for the older customer. This can often take substantial amounts of time to fix and test. One possible solution is to keep individual copies of the library for each customer and apply bug fixes individually for each customer. Either way, problem four is: Generic reusable libraries can become a maintenance nightmare.
So to summarise, some of the common problems facing code reuse are:
- Making code reusable takes effort and time at the outset.
- Generic reusable interfaces are hard to develop.
- Each time a piece of code is reused, it usually still requires some tweaking.
- Reusable code can often turn into a maintenance nightmare.
When does reuse work
Code reuse doesn't always fail. We actually reuse code all the time without even thinking about it. As our experience grows as developers we learn new and better ways of approaching problems, and we reuse these ideas and techniques.
(... will add more later) ~Ryan Mallon