MSc-IT Study Material
January 2011 Edition

Computer Science Department, University of Cape Town
| MIT Notes Home | Edition Home |

Design patterns in object-oriented programming

Definitions of terms and concepts

The following is a summary of terms you were already introduced to in the earlier chapters that will be essential for the understanding of design patterns.

Object

One of the main tasks of object-oriented design is to identify the classes which make up the software system (see Chapter 5, Object-oriented Analysis and Design).

Not all objects that will be part of a system are identified early on in the development process, for a number of reasons, including the chosen software process (such as incremental processes).

Interface

The most important aspect of an object is its interface. An object's interface defines how the object can be used, in other words, to what kind of messages it can respond. The parameters that need to be passed with the message, if any, and the return type are called collectively the operation's signature. The implementation details of these operations do not need to be known to the client.

Many operations with the same name can have different signatures, and many operations with the same signature can have different implementations (using inheritance). These are forms of polymorphism. This substitutablity — in other words, being able to substitute objects at execution time — is called dynamic binding, and is one of the main characteristics of object-oriented software. Objects with identical sets of signatures are said to conform to a common interface.

Class

A class definition can be used as a basis for defining subclasses by means of inheritance. A subclass possesses all the data and method implementations of the superclass together with additional data and methods pertaining exclusively to objects of the subclass. In some cases, subclass data may shadow superclass data with the same identifiers, or may override methods with the same signature. An abstract class is a class that can have no objects. Its main purpose is to define a common interface shared by its subclasses. Sub-classes specify implementations for these the methods of an abstract class by overriding them.

There is a distinction between inheritance and conformance. In Java, this is explicitly defined by means of extending a class through inheritance, and by implementing an interface to ensure conformance to certain behaviour. An object's type is defined by its interfaces; this defines the messages to which it can respond or, in other words, how it can be used. A class is a type, but objects of many different classes can have the same type.

Scope of development activity: applications, toolkits, frameworks

Software developers may find themselves involved in different sorts of software development activities. Most developers work on applications designed to be used by non-specialist computer users to perform tasks relevant to their particular work. However, some developers may be involved in producing specialist software designed to help application software developers in the production of their applications. The products of such developers are variously called toolkits or frameworks, depending on the scope of their applicability.

When developing an application it is necessary to consider reusing existing software, as well as making sure the newly developed software is easy to maintain and is itself reusable. Maintenance is in itself a form of software reuse.

The smallest unit of reuse in object-oriented software is an object or class. When a class is reused (e.g., refined by means of sub-classing) this is called white-box reuse. This is due to visibility: all attributes and methods are normally visible to sub-classes. This type of reuse is considered to be more complex for developers, because it requires an understanding of the implementation details of the existing software. When reuse is by means of object composition, and we are only concerned with the interfaces – how an object can be used — this is called black-box reuse, because the internal details of the object are not visible.

Black-box reuse has proved to be much more successful than white-box reuse. It is less complex for developers and does not interfere with the encapsulation of objects and is therefore safer to use.

Toolkits are a set of related and reusable classes designed to provide a general purpose functionality. Toolkits help with the development process without imposing too many restrictions on the design. The packages in Java such as java.net, java.util, and the java.awt are examples.

Frameworks represent reuse at a much higher level. Frameworks represent design reuse and are partially completed software systems intended for a specific family of applications. One example of a framework is the Java Collections Framework.

Patterns, in contrast, are not pieces of software at all. They are more abstract, intended to be used for many types of applications. A pattern is a small collection of objects or object classes that co-operate to achieve some desired goal. Each design pattern concentrates on some aspect of a problem and most systems may incorporate many different patterns.

Pattern classifications and pattern catalogue

Design patterns are based on practical solutions that have been successfully implemented over and over again. Design patterns represent a means of transition from analysis/design to design/implementation.

To help developers to use design patterns, catalogues of patterns have been created. Each catalogue entry for a pattern should contain the following four essential elements:

  • The pattern name, which identifies a commonly agreed meaning and represents part of the design vocabulary.

  • The problem or family of problems and conditions to which it may be applied.

  • The solution, which is a general description of participating classes/objects and interfaces their roles and collaborations.

  • The consequences — each pattern highlights some aspect of the system and not others, so it is useful to be able to analyse benefits and restrictions.

Gamma et al classify design patterns into three categories according to purpose. The categories are behavioural, creational and structural. Unfortunately the catalogue of patterns is not standardised, which may cause some confusion. The level of granularity and abstraction differs greatly from objects whose only responsibility is to create other objects to those that create entire applications. There is no guarantee that a suitable pattern will always be found. It also may be that several different patterns could be used to solve a specific problem — in other words, a single pattern may not represent the only solution, but a possible solution.

Table 8.1. Design patterns according to Gamma et. al.

BehaviouralCreationalStructural
InterpreterFactory MethodAdaptor (class)
Template MethodAbstract FactoryAdaptor (object)
Chain of ResponsibilityBuilderBridge
CommandPrototypeComposite
IteratorSingletonDecorator
Mediator Facade
Momento Flyweight
Observer Proxy
State  
Strategy  
Visitor  


The Portland Pattern Repository

A large collection of design patterns is available at the Portland Pattern Repository. This repository is hosted by Cunningham & Cunningham, the consultancy firm of Ward Cunningham, one of the Gang of Four. The website is also famous for being the web's first wiki.

Behavioural patterns

Behavioural patterns are required when the operations that need to be performed cannot be achieved without co-operation. Thus behavioural patterns concentrate on the way in which classes and objects organise responsibilities in order to achieve the required interaction.

The Observer pattern is an example of a behavioural pattern that defines some dependency between objects.

The Observer pattern

In some applications, two or more objects that are independent of each other must respond to some event in synchrony. For example any Graphical User Interface (GUI) will respond to the click of a mouse button, or keyboard, which will trigger the execution of an application or utility and redraw the screen appropriately. The mouse click event will result in one or more objects responding. Each object is otherwise independent. Each object is able to respond only to certain events.

Other typical examples of applications in which the Observer pattern could be used:

  • To integrate tools in a programming environment. For instance, the editor for creating program code may register with the compiler for syntax errors. When the compiler encounters such an error, the editor will be informed and can scroll to the appropriate line of code.

  • To ensure consistency constraints, such as referential integrity for database management systems.

  • In the design of user interfaces to separate presentation of data from applications that manage the data. For example a spreadsheet object and a chart or a text report may all display the same information resulting from an application's data at the same time in their different forms.

The problem

The important relationship that needs to be established is between a subject and an observer. The subject may be observed by any number of observers. The observers should be notified when the subject changes state. Each observer will receive information concerning the subject's state with which to synchronise, and to allow them to respond as required by the application. The following summarises the conditions:

  • The subject is independent, and the observers are dependent.

  • A change in the subject will trigger changes in observers — of which there may be many.

  • The objects that will be notified by the subject are otherwise independent. They only share in some aspect of their behaviour.

Figure 8.8. Class diagram of the Observer pattern

Class diagram of the Observer pattern

The solution

Participating classes /objects:

In the diagram the subject is shown as a class. Subject has methods for attaching and detaching observer objects. The methods are shown on the diagram as addObserver(), deleteObserver() and notifyObservers().

Observer has an updating interface for objects that will be notified of changes in a subject, here shown as an interface with the method update().

Concrete Subject, a subclass of Subject, contains its state (of interest to the observers), plus operations for changing its state. Concrete Subject is able to notify its observers when its state changes. On the diagram the attribute state, and methods getState(), setChanged() provide this functionality, and the other methods are inherited from Subject.

Concrete Observer maintains a reference to the Concrete Subject object, and a state that needs to be kept consistent with the Concrete Subject. It implements the updating interface. On the diagram the Concrete Observer is a class that implements the Observer interface. It supplies the code for the update() method and has observerState to denote the data that needs to be kept consistent with the Concrete Subject object.

Collaborations

The Concrete Observers objects register with the Concrete Subject object, using the addObserver() method.

When a Concrete Subject changes state it notifies the Concrete Observer objects by executing the notifyObservers() method.

The Concrete Observer object(s) obtain the information about the changed state of the Concrete Subject and execute the update() method.

Consequences

The advantage of the pattern is that the Subject and Observer are independent of each other, and the subject does not need to know anything about the handling of the notification by the observers (i.e., how update() works). This means that any type of broadcasting communication could be implemented in this way.

Creational patterns

Creational patterns handle the process of object creation. These patterns may be used to provide for more reusable designs by placing the emphasis on the interfaces and not the implementation. The abstract factory is an example of a creational pattern that can be used to make objects more adaptable, in other words:

  • Less dependent on specific implementations.

  • More amenable to change and customisation, easier to change the objects themselves.

  • Less necessary to change the applications that use the objects.

Abstract factory pattern

The abstract factory pattern makes the system independent of how objects are created, composed and represented. It should be used whenever the type or specific details of the actual objects that need to be created cannot be predicted in advance, and therefore must be determined dynamically. Only the required behaviour of the objects is specified in advance. The information that can be used to create the object would be based on data passed at execution time. Examples of applications of the pattern:

  • To customise Windows, Fonts, and so on, for the platform on which the application will run so as to ensure appropriate formatting, wherever the application is deployed.

  • When the application specifies all the required operations on the objects it will use, but their actual format will be determined dynamically.

  • To internationalise user interfaces (e.g., to display all the text in a local language, to customise the date format, to use local monetary symbols).

The problem

The application should be independent of how its objects are created and represented. It should be possible to configure the application for different products/platforms. The application defines precisely how the objects are used (i.e., their interfaces).

The solution

Participating classes/objects

Abstract Factory class will contain the definition of the operations required to create the objects, createProductA(), createProductB().

Concrete Factory implements the operations createProductA(), createProductB(). Only one Concrete Factory is created at run time which will be used to create the product objects

AbstractProduct will declare an interface for the type of product object for example a particular type of GUI object: Label or Window.

ProductA will define the object created by the Concrete Factory 1, implementing the Abstract ProductA interface.

Client Application uses only the interfaces from the Abstract Factory and Abstract Product classes.

Figure 8.9. Class diagram for the Abstract Factory pattern

Class diagram for the Abstract Factory pattern

Consequences

Different product configurations can be used by replacing the Concrete Factory an application uses. This is a benefit and liability, because for each platform or family of products a new Concrete Factory subclass needs to be defined. However, the changes will be broadly restricted to the definition of subclasses of Abstract Factory and Abstract Product, thus confining the changes to the software to well documented locations.

Structural patterns

These patterns deal with the composition of complex objects. Similar functionality can be often achieved by using delegation and composition instead of inheritance. An example of a structural pattern is the composite pattern. In the Java API, this pattern is used to organise the GUI using AWT objects and layout managers.

Composite pattern

The pattern involves the creation of complex objects from simple parts using inheritance and aggregation relationships to form treelike hierarchies.

The diagram shows the composite pattern as a recursive structure where a component can be either a leaf (which has no sub-components of its own) or a composite (which can have any number of child components). The component class defines a uniform interface through which clients can access and manipulate composite structures. In the diagram this is represented by the abstract method operation().

Figure 8.10. Class diagram of the Composite pattern

Class diagram of the Composite pattern

The problem

Complex objects need to be created, but the composition and its constituent parts should be treated uniformly.

The solution

Participating classes/objects

Component should declare the interface for the objects in the composition, as well as interfaces for accessing and managing its child components.

Leaf represents objects that have no children, and defines behaviour for itself only.

Composite will define behaviour for components with children, and implements the child related interfaces.

Client Application manipulates objects through the component interface using the operation() method. If the object to be manipulated is a Leaf it will be handled directly, if it is a Composite, the request will be forwarded to the child

Consequences

The pattern enables uniform interaction with objects in a composite structure through the Component class.

Defines hierarchies consisting of simple objects and composite objects which can themselves be composed and so on.

Makes it easier to add or remove components.

How to use a design pattern

  • Consult design pattern catalogues for information (such as the Portland Pattern Repository, discussed earlier). You may find an example or description that may suggest the pattern is worth considering.

  • Try to study the suggested solution in terms of participating objects/classes, conditions, and descriptions of the collaborations.

  • If the examples of these patterns are part of a toolkit, it may be useful to examine the available information. java.util supports the Observer pattern, for example.

  • Give participant objects names appropriate for your application context.

  • Draw a class diagram showing the classes, their necessary relationships, operations and variables that are needed for the pattern to work.

  • Modify the names for the operations and variables appropriately for your application.

  • Try out the pattern by testing a skeleton example.

  • If successful refine and implement it.

  • Consider alternative solutions.