Some Common Design Patterns
Here's a good reference page on design patterns -- lists out many
common patterns, with good descriptions
Facade Pattern
Problem Description / Context
- A subsystem contains mulitple classes, each having functionality
needed by other subsystems
- Desire to reduce coupling between this subsystem and others
- Would like to be able to change implementation of subsystem in the
future, while still providing consistent functionality through a
stable interface
Solution
- Define a single class (the facade class) to be the high-level
interface into the subsystem
- All external users of the subsystem will make calls through the facade
class -- the entry point into the subsystem
- The facade class methods will delegate requests to the internal
subsystem classes
Consequences
- Clients are shielded from the low-level classes in a subsystem
- Simplifies the subsystem usage with a simpler interface
Example
A compiler subsystem (illustrated in the textbook, page 701)
- Consider a compiler subsystem that has several classes, including:
Lexer, Parser, ParseTree, Linker, CodeGenerator, Optimizer
- It would be more difficult for an external subsystem to have to
communicate with all these classes individually
- So, we create a facade class -- Compiler -- to act as
the entry point for all these tools.
- Caller only deals with Compiler class, which delegates the
requests to appropriate methods of the contained classes
Adapter Pattern
Problem Description / Context
- Would like to use an existing class (perhaps a legacy class) without
modifying it
- However, the desired usage requires conformance to different target
interface (to be used by the client)
Solution
- Define an adapter class that acts as a go-between
- Adapter will translate client requests to the legacy (or adaptee)
class
- Usually, an adapter class just provides the expected interface to the
client, and translates to appropriate calls to the adaptee's interface.
Primarily a simple translation of calls.
- Often known as a wrapper class
Consequences
- The client and the adaptee class work together without modification to
either
- The adapter class works with the legacy/adaptee class and all of its
subclasses
- any subclasses of the client class would need a new adapter to work
with the legacy class
Example
Bridge Pattern
Problem Description / Context
- Separate an interface from implementation in a way that different
implementations could be substituted
- Similar purpose as the Adapter pattern, except not constrained by
existing components
- Adapter is for filling the gap between different interfaces
- Bridge is for filling the gap between interface and
implementation
Solution
- Create an abstract class or interface construct (in Java), which
defines an interface visible to client classes
- Could have multiple possible concrete implementing classes (using the
same interface).
- Could also have an abstract implementor class (base class of various
concrete implementations), which is called upon generally by the
high-level interface
Consequences
- client is shielded from concrete implementations, client only sees
high-level interface
- Different concrete implementations could be substituted, or refined
independently from the high-level interface
- Lower level interfaces could also be refined, as long as high-level
interface is not changed
Examples
- A simple example of this concept is what the Java interface
construct does
- Recall that a Java interface is like an abstract class whose methods
are all abstract
- Different classes can implement the same interface, and will then
have their own concrete versions of the interface methods
-
This page has a simple example, using a Java interface
- Textbook gives a more complex example starting on page 317
Strategy Pattern
Problem Description / Context
- A class (a Context class) could benefit from different
variants of an algorithm, perhaps to be interchangeable at run time
- There could be multiple ways to code an algorithm, each one more
efficient under certain circumstances
- Clients of the class might want to supply custom versions of the
algorithm
- The idea is to de-couple the policy deciding classes from the actual
implementing mechanisms
Solution
- define an abstract class / interface (an abstract strategy
class), which describes the interface common to all mechanisms (or
algorithms) used by the calling context
- Concrete strategy classes can be created as various implementations of
this abstraction
- Client of the context class can supply a concrete strategy object
- Context class calls upon the appropriate methods, which are specified
in the abstraction
Consequences
- Concrete strategy implementations can be substituted transparently
from the context
- Different algorithms could be chosen (even at runtime) and plugged in,
based on current conditions (speed of algorithm on a given system, for
example)
- New algorithms can be added without modifying the context or the
client (or the strategy interface)
Example
This example given in the textbook references a common Java method. In
the text, it is listed as an example of the Adapter pattern, but
it really seems to better fit the Strategy pattern. (Note that there are
patterns with similar concepts. Strategy Pattern has similarities with
both Adapter and Bridge patterns).
- In java class java.util.Arrays, there are some static
methods called sort(), for sorting arrays
- The sort() methods with one parameter just take in an array,
and it sorts the array based on the natural ordering
- Objects have a natural ordering if they implement
Comparable, overriding the compareTo() method
- If another custom ordering is desired, there's a sort()
method with two parameters: the array of objects, and an object that
implements the Comparator interface. This interface
has a method called compare()
- The coder needs to create a class that implements
Comparator. Such a class carries in the custom
compare() algorithm into the Arrays.sort()
method
Observer Pattern
Problem Description / Context
- One object, the subject (our text calls the publisher),
maintains some state and/or is the source of specific events
- Other objects, the observers (our text calls them
subscribers), wish to know when something happens to the subject
(like a state change or an event), so that they can respond and maintain
consistency
Solution
- Create an observer (subscriber) interface type (interface or abstract
class)
- Concrete observer objects are derived from this abstraction
- The subject (publisher) class provides methods for attaching observers
(i.e. allowing observers to register with the subject)
- When an event occurs, or the subject's state changes, a notification
is sent to all observers
- Could be done by the subject invoking a notify() method...
- ... which can call individual update() methods for each observer
Consequences
- Decouples the subject from the observers
- Could result in too many extra broadcasts when the state of the
subject changes
Example
Event listeners in Java
- Used when a Java GUI component wants to handle possible events (like
button clicks, mouse events, etc)
- The component (like a Button) is the subject that the event might
occur on
- There are listener interfaces -- implementing objects are the
observers
- The GUI component has to call an add[Something]Listener
method, which registered the appropriate observer (i.e. event listener)
with the component
- When an event or the appropriate type occurs, the listener object is
informed, and it invokes a handler function
Command Pattern
Problem Description / Context
- Want to encapsulate requests so that they can be executed, undone,
queue, etc. independently of the request.
- This would be making commands behave like objects -- can store
additional info with them or put them into collections
Solution
- Create a Command abstraction (abstract class / interface), with
a method to execute the command (like execute())
- Supply other desired methods in this abstraction that do any desired
state manipulations (of a command object)
- Each concrete command class type will implement this abstraction
(implement interface or derive from abstract class)
- The client can create command objects, which can be invoked through
the generic interface
Consequences
- The object of the command and the algorithm are decoupled
- Concrete commands are objects, which can be created and
stored
- New commands can be added without changing the existing code
Examples
- Suppose we want to implement a game where not only the most
recent command can be undone, but we'd like to keep a history of
commands, so that we can undo back to an earlier point in the game
- Commands as objects -- these can be placed in a stack, which tracks a
command history. If we want an undo feature, we pop the last command off
the stack and call an undo() method for it
- An example used in the textbook is the sample ARENA system
- The command pattern is used to decouple the Moves from the actual
Games in the ARENA system -- see page 339
- Abstract class Move.
- Concrete command classes TicTacToeMove, ChessMove,
for example
- The Match class can be written generically (this class
invokes the commands) without worrying about which game is being
played
Composite Pattern
Problem Description / Context
- Primitive objects can be combined into composite objects
- Clients treat composite object as a primitive object -- i.e. we'd like
to be able to use some of the same basic interface for both composite and
the primitives
Solution
- Define an abstraction (abstract class / interface) for the basic
object type -- we'll call it a Component interface
- Single objects (leaves) and composites should both inherit from
that interface
- The composite object can contain (through aggregation) links to the
leaves (the primitives), or even other composites
- The composite class implements the methods (inherited from the
Component abstraction) by applying the methods from the contained
primitives and combining the results
Consequences
- Client uses same code (same interface) for dealing with the primitives
(the leaves) or the composites
- Leaf-specific behavior can be modified without changing the
hierarchy
- New leaves can easily be added
Examples
- Consider a hierarchy of Employee objects, where each Employee
also needs to keep track of a list of subordinates (other employees that
they manage)
- Build a base abstract class Employee
- Derive subtypes from this base, for different categories of
employees (managers, supervisors, drones...)
- Some subtypes won't have a subordinate list (i.e. your basic worker
drone)
- Each object that stores information about underlings does so with
aggregation. Example: Keep a private data tree of
references/pointers to subordinate employee objects
- Client classes can call upon the same basic interface for any object
in the hierarchy -- regardless of whether it contains a subordinate
list or not.
- Hierarchies of files and directories. Directories can contain files
and other directories. Basic command structure is the same -- same
operations available for moving, renaming, deleting, etc, on files and
directories
- Java GUI components use this pattern, too.
- There's a base abstract class called Component
- There's a hierarchy of component types, including primitive
components (like buttons, labels, checkboxes), and containers
(frames, panels, applets)
- The containers are the aggregates -- they can contain other
components, including primitives AND other containers
- Example: A frame contains two panels. First panel contains several
checkboxes. Second panel contains 3 buttons
- Since these are all derived at some point from Component,
they all share the same interface methods that come from
Component. Clients can make these calls to any of the
component or container objects. (Ex: paint(),
setSize(), setVisible(), etc)