We start with the reuse mechanisms built in to object-oriented programming.
Patterns are classified by their scope and their purpose.
A design pattern's scope specifies whether it applies primarily to classes or objects: Most patterns are object patterns.
A design pattern's purpose reflects what it does:
Patterns described by GHJV95:

Cross-classifications:

Object-oriented design can reuse code through:
To understand design patterns one must understand the difference between class inheritance and interface inheritance.
New classes can be defined in terms of existing classes using class inheritance (also called implementation inheritance):

Class inheritance is basically just a mechanism for extending an application's functionality by reusing functionality in parent classes. Class inheritance defines an object's implementation in terms of another object's implementation.

In short, it's a mechanism for code and representation sharing.

Inheritance can also be used to define families of objects with identical APIs by inheriting from a purely abstract class.

Interface inheritance (or subtyping) describes when an object can be used in place of another.

     Parent y;
     ...
     y = new Child1();
     y.method1();       // some behavior
     ...
     y = new Child2();
     y.method1();       // different behavior
	   
Interface inheritance so greatly reduces implementation dependencies between subsystems that it leads to the following principle of reusable object-oriented design:


Program to an interface, not to a class (or an implementation)
 

Don't declare a variable to be an instances of particular concrete classes if you need to assign various implementations of an interface to it.
     Child1 y;          // Bad?
     ...
     y = new Child2();  // Error: Can't assign Child2
	   

Instead, commit only to interface types.

Reuse by subclassing (class inheritance) is often referred to as white-box reuse: Code reuse can also be obtained by composing objects out of other objects, i.e., by creating object associations (or aggregations).

Object association is referred to as black-box reuse:

An example of how class inheritance is white-box reuse:

Since the internal details of the stack are exposed as a vector, stack encapsulation has been broken

The preferred way to reuse the capabilities of a vector when implementing a stack:


Favor object association over class inheritance.
 

Using association promotes class cohesion: keeping classes encapsulated and focused on one task.

This principle also promotes smaller hierarchies and dynamic behavior.

Favoring association over inheritance promotes smaller hierarchies because classes and class hierarchies will remain small and less likely to grow unmanageably.

Here is an example of an inappropriate use of class inheritance:

Two problems with this:

  1. Classes are so similar there would be little difference in their operations
  2. Every time a new kind of recording is conceived, a new class must be created.

When a new kind of recording is conceived, a new RecordingCategory object is created with an appropriate label.

New kinds of recording generate new objects and relationships, but no new classes are created:

If the associated objects are related by an interface type, many different objects can be associated, allowing many kinds of behavior.

Recall graph search example:

Dynamic behavior is a common objective in behavioral design patterns (see the Classification menu item).

Dynamic behavior involves a Delegator participant and a Delegatee participant, with the Delegator delegating part of its responsibility to the Delegatee.

Dynamic behavior enhances flexibility and easy reuse in several contexts, including when:

In any delegation the Delegator needs an instance variable to reference the Delegatee.

To achieve the flexibility goals of delegation, "Delegatee" just defines a common interface for a variety of concrete classes that implement different behaviors.

We refer to these concrete classes as ConcreteDelegatees, as shown below.

We will find that delegation plays a role of sub-pattern in several of the design patterns to follow.

Some of the behavioral and creational design patterns incorporate the general delegation idea.
Behavioral design patterns incorporating delegation include:
Creational design patterns incorporating delegation include:
Following is a list of the GHJV design patterns labeled by purpose, where the labels mean:

Behavioral
Structural
Creational

Participants

This example shows a chain of objects each of which knows how to handle a certain kind of integer.

If an object is given an integer it doesn't know how to handle, it passes responsibility to the next object in the chain.

Following are files that implement the structure.
Exceptions may be implemented using the Chain of Responsibility pattern, though the objects involved may not be visible to programmers:
Document styles such as Content Style Sheets (CSS) often support style inheritance:
Inheritance mechanisms in classless OOLs may use a Chain of Responsibility design pattern to minimize the memory footprint of objects.
You want toimplement commands that behave like objects, either because you want to store additional information with commands, or you want to collect commands.

Example

Provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation.

Participants

  • Aggregate: defines an interface for creating an Iterator object.
  • Iterator: defines an interface for accessing and traversing elements in an Aggregate.
  • Concrete Aggregate: implements the Iterator creation interface to return an instance of the appropriate ConcreteIterator.
  • Concrete Iterator: implements the Iterator interface, keeping track of its current position in the Aggregate.

Example

java.util.Iterator
Without violating encapsulation, capture and externalize an object's internal state so that the object can be restored to this state later.

Participants

  • Originator: provides a method that creates a Memento and a method with a Memento parameter that can be invoked later.
  • Memento: stores all or part of the internal state of the Originator object.
  • Caretaker: obtains the Memento from the Originator and later invokes an Originator method to restore its state, using the Memento as a parameter.

Example

Often used to support "undoable" operations in editors:
  • The Memento object records information about the state of the document that is being edited.
  • The editor restores this state to undo an earlier operation.
See Swing's UndoableEdit interface.

Participants

  • Delegator: Subject
  • Delegatee: Observer
  • ConcreteDelegatee: ConcreteObserver

Example: Swing Listeners

Example: Model-View Separation

Participants

  • Delegator: Context
  • Delegatee: State
  • ConcreteDelegatee: StateA, ...

Example: Network Connections

Participants

  • Delegator: Context
  • Delegatee: Strategy
  • ConcreteDelegatee: ConcreteStrategyA, ...

Example: LayoutManager

Sometimes you want to encapsulate a group of classes in order to implement an abstract view of their combined functionality.

Participants

  • Facade: delegates client requests to the appropriate subsystem objects.
  • Subsystem classes: implement subsystem functionality.

Examples

Swing JTable, JTree, and JEditorPane.

Each of these examples combines an implementation of JComponent methods with Facade constructors and methods whose abstract view hides complex model, view, and controller classes.

To make an object a (possibly lightweight) placeholder for another object.

Example

Participants

  • Delegator: Client
  • Delegatee: AbstractFactory
  • ConcreteDelegatee: ConcreteFactory

Examples

Interface javax.swing.text.ViewFactory:

A factory to create a view of some portion of document subject. This is intended to enable customization of how views get mapped over a document model.

Participants

  • Delegator: Director
  • Delegatee: Builder
  • ConcreteDelegatee: ConcreteBuilder

Examples

SAX parsers for XML.
How to supply a method that can be overridden to create objects of varying types.

Example 1

Collection.iterator()

Example 2

Move.doMove(State)

The goal of data abstraction in general is the clear separation of data's use from its implementation.

Data abstraction is achieved in object-oriented languages through encapsulation of state and behavior: making an object's internal state inaccessible directly, so that:

  • It's representation is not visible from outside the object
  • Data can only be accessed through available methods
Complex programs often need multiple levels of encapsulation, provided by encapsulation design patterns, that work by either:
  • Providing a new form of encapsulation, or
  • Protecting a high-level encapsulation by relaxing encapsulation at a lower level
  • Facade: provides a unified interface to a set of interfaces in a subsystem making it easier to use
  • Strategy: encapsulates an abstract behavior that has several variations.
Sometimes you need to relax encapsulation at a low level in order to do a better job of encapsulation at a higher level.

Although this sounds contradictory, the improvement arises from the fact that allowing higher level classes to directly access data in lower level classes or vice-versa avoids having to declare public methods for the access.

For this reason, well-designed OOLs have mechanisms that allow one class to have access to private members of another class.

  • In C++ you can declare one class as a "friend" of another class.
  • In Java, a class can be nested inside of another class, which allows both classes to access private members of the other class.
Three design patterns that employ relaxed encapsulation:
  • Observer: often provides a public method for modifying an object that it has private access to, but another object (a Subject) decides when the modification will occur
  • Iterator: provides sequential access to objects in an Aggregate without exposing the implementation of the Aggregate
  • Memento: carries state information about an Originator object, allowing the Originator private access to that information