Design Patterns  «Prev  Next»
Lesson 2Designing reusable classes is challenging
Objective Learn the Problem that Design Patterns solve.

Challenges when Designing Reusable Classes

Code reuse is alleged to be one of the prime benefits of object-oriented programming. Rather than solving a problem from scratch, you simply derive a subclass[1] from an existing class and override a few methods. The reality is considerably less promising than the theory.
Although design patterns describe object-oriented designs, they are based on practical solutions that have been implemented in mainstream object-oriented programming languages like Java and C++ rather than procedural languages like ( C, Fortran 77, and Cobol). This site chose Java and C++ for pragmatic reasons:
Our day-to-day experience has been in these languages, and they are increasingly popular. The choice of programming language is important because it influences one's point of view. Our patterns assume Java and C++ level language features, and that choice determines what can and cannot be implemented easily. If we assumed procedural languages, we might have included design patterns called "Inheritance," "Encapsulation," and "Polymorphism. But since these OO-Design Principles are already part of the Java language, these elements are not classified as patterns.

The fact is, designing classes is hard, and designing reusable classes is even harder. The best estimates reveal that even experienced object-oriented programmers take three times as long to design a class that can be reused in other contexts as they do to solve the immediate problem they face.
Given this time cost, it is no wonder that few programmers explicitly code for reuse. Software projects are routinely understaffed, under-budgeted and over deadline. Although a class you actually reuse can repay its cost of development on the fourth project, few organizations have the human capital needed to invest in truly reusable code. In fact, reuse is often considered only by companies developing class libraries for third party use, where the extra cost of designing for reuse can be repaid hundreds of times over on many thousands of different projects.

Object-oriented Design

When designing a system for an object- oriented implementation, you must:
  1. Identify the participants in the system: The participants in the system can be thought of as the actors participating in a use case.
  2. Factor them into classes at the right level of granularity
  3. Define the public interface of each class:
  4. Establish inheritance hierarchies
  5. Define the relationships between classes

Experienced developers may be able to build a working solution through this procedure. However it is almost unheard of for even experienced designers to build a reusable solution on their first attempt. Before you can build an extensible, reusable system, you are probably going to have to build a prototype to find out where your errors and misconceptions are.
The purpose of the prototype is get the basic functionality off the ground and to see if your "proof of concept" can be enhanced using future iterations. Once you understand design patterns and have had a mind altering experience with them, you will not ever think about object-oriented design in the same way. You will have insights that can make your own designs more flexible, modular, and reusable, which is why you're interested in object-oriented technology in the first place.

Abstract Data Types, Interfaces, and Pre- and Postconditions

One of the goals of object-oriented programming is to write reusable code, which is code that can be reused in many different applications, preferably without having to be recompiled. One way to make code reusable is to encapsulate or combine a group of data elements representing an object together with its operations (functions and operators) in a separate program module (a class). A new program can use the functions to manipulate the data without being concerned about details of the data representation and the implementation of its operations. In this way, the class can be used as a building block to construct new application programs. The combination of data together with its operations is called an (ADT) abstract data type .
Figure 2.2 shows a diagram of an abstract data type. The data values stored in the ADT are hidden inside the circular wall. The bricks around this wall are used to indicate that these data values cannot be accessed except by going through the ADT's operations. A class provides one way to implement an ADT in C++. If the data fields are private, they can be accessed only through public functions. Therefore, the functions control access to the data and determine the manner in which the data is manipulated. A primary goal of this course is to show you how to write and use ADTs in programming. As you progress through this course, you will create a large collection of ADT implementations (classes) in your own program library.
Because each ADT implementation in your library will already have been coded, debugged, and tested, using them would make it much easier for you to design and implement new application programs. In addition, the C++ Standard Library provides a rich collection of ADT implementations. You will be introduced to many of the ADTs and design patterns as you progress through this course.

Diagram of an ADT
Figure 2.2 Diagram of an ADT

[1]subclass: A class that is derived from a particular class, perhaps with one or more classes in between.