Design Patterns

What Are Object-Oriented Design Patterns?

Definition A pattern of fixed class structure (think UML class sub-diagram) and messaging (sequence diagram) that repeatedly pops up as a very useful programming idiom.

The Gang of Four Patterns

We will cover the patterns in the order they are presented in the HFDPs book.

Observer

See HFDPs Chapter 2.

Go4 description: Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.

Diagram

Decorator

See HFDPs Chapter 3

Go4 description: Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality.
Diagram

Design Principle: The Open-Closed Principle

This principle is illustrated well by the decorator pattern; we covered it in the design lectures but will revisit it now.

Factories

See HFDPs Chapter 4.

  • a Simple Factory is any method that makes objects of more than one possible class. Book example with pizzas.
  • Also there are two official design patterns, Factory Method and Abstract Factory. General issues with factories:

    Factory Method

    A factory method is a generalization of a simple factory:
    • There is a superclass or interface which has the factory or method declared
    • Subclasses or implementers specialize this factory to create the kinds of objects they need

    Go4 description: Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.
    Diagram

    • Pizza example in HFDPs
    • Example in Swing: createDefaultModel() in JtextField is a factory method to return a default model for your input field; you can override this in your subclass MyJTextField of JTextField to create your own model for the textfield, which e.g. allows integer inputs only. (This example is a slight variation on the pattern in that that the FactoryMethod is not abstract in the superclass; instead, it has a default behavior which can be overridden).

    Design Principle: The Dependency Inversion Principle

    • We covered this earlier but now we can more clearly see what it means
    • The initial pizza-making code without a factory required the top-level PizzaStore class to itself choose the kind of pizza: see picture from book.
    • Arrows in the above (read as "dependent-on") show high-level code directly dependent on details of low-level code (it needs their names to create them via new)
    • This is backwards, the high-level code should not depend on low-level; instead, low-level should depend on high level.
    • This picture shows how the depenencies got inverted whan a factory method is used
    • The dependencies were inverted - D-I-P!

    Abstract Factory

    Go4 description: Provide an interface for creating families of related or dependent objects without specifying their concrete classes.
    Diagram
    • Abstract Factories for Pizzas
    • The key difference here compared to Factory Method is the FAMILY of related classes getting created.
    • Another simple example: Child has subclasses Boy and Girl; you have various supplies such as Toothbrush, Sheet, etc you need to make which are very similar, but the BoyToothbrush is the car toothbrush and the GirlToothbrush is the Hello Kitty one.
    • Example: For a distributed game, there can be a Player (A) which can be LocalPlayer (A1) or a DistributedPlayer (A2), and a Board (B) which can be a LocalBoard (B1) or DistributedBoard (B2). The abstract factory can polymorphically make either a local or distributed player. It is polymorphic because the holder of the factory need not know whehter its local or distributed; the factory itself holds that information.

    Singleton

    HFDPs Chapter 5. Go4 description: Ensure a class has only one instance and provide a global point of access to it.
    Diagram
    • For a class with only one member, something quite common in practice.
    • Code it so that multiple instances can't be created.
    • Have a class method to get the sole instance.
    Question: why not just use static methods and create no instances?
    Answer:
    • Static methods are directly globally visible, violating encapsulation boundaries.
    • You can't pass classes as arguments; also no object polymorphism with classes
    • This will not be extensible if for some reason two are needed.

    Command

    HFDPs Chapter 6

    Go4 description: Encapsulate a request on an object, thereby letting one parameterise clients with different requests, queue or log requests, and support undoable operations.
    Diagram

    • In this diagram, Invoker wants to perform a command, ConcreteCommand is the object containing the actual command details (abstractly shown here to be a message to Receiver), and Command is an abstract superclass so we can generically support a multitude of concrete commands. Ignore Client.
    • Since these commands are now objects we can put them on a log queue, use that queue to implement an undo() action, even make a decorator for commands, etc etc.

    Adapter

    HFDPs Chapter 7

    Go4 description: Convert the interface of a class into another interface clients expect. Adapter lets classes work together that couldn't otherwise because of incompatible interfaces.
    Diagram

    • First, the case when an adapter isn't needed is when there is a common interface and the object you want to use implements this interface.
    • Second, there is the case when the interface is incorrect or incomplete for uses, but you can easily change it and that will solve the problem -- do it!
    • The third case is the bad thing: there is an interface, but it isn't quite what you need and either you can't change it or if you changed it to the way you wanted it would mess up some other code.
      • It may have method names slightly different;
      • It may be missing one of the methods and so some default value must be popped in for that method
      • Some translation of the data may be needed, e.g. your interface expects rectangular coordinates for a point and the object you want to use expects polar coordinates.
      • You may want to add some extra functionality in the Decorator spirit, e.g. log requests.
    • Then, the solution in the above case is to create a new intermediate object, the adapter, which serves to adapt the interface for that object.
    • The book has a good example of how the old Java Enumeration class can be adapted to work like the new Iterator class.
    • (Note the Swing MouseAdapter etc classes are somewhat different than this notion of adapter, they are not a forwarding intermediary -- the use of "Adapter" there is thus confusing)

    Façade

    HFDPs Chapter 7

    Go4 description: Provide a unified interface to a set of interfaces in a subsystem. Façade defines a higher-level interface that makes the subsystem easier to use.

    • A facade turns a bunch of wild objects into a component, by putting the interface of interaction with those objects on one object, the facade.
    • Facade objects don't do the work, they just delegate (forward messages) to other objects.
    • Facades can also do adapting, such as supplying some of the arguments to lower-level methods, invoking multiple methods, etc.
    • Example: Home theater system example from book
      • Before: user needs to interact with all the individual components
      • After: the HomeTheaterFacade provides the interface to all the classes to outsiders; outsiders need to know nothing about the internals.

    Design Principle: The Principle of Least Knowledge

    Talk only to your immediate friends
    We covered this earlier, but there is one more point to make:
    • Facade is a great pattern primarily because of how effectively it implements this principle.
    • The "users" of the large collection of objects under the facade only need to talk to the facade object (their immediate friend) and not all of the other objects.

    Template Method

    HFDPs Chapter 8

    Go4 description: Define the skeleton of an algorithm in an operation, deferring some steps to subclasses. Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithm's structure.
    Diagram

    • The template is an algorithm with "holes" in the superclass
    • the subclasses implement the "holes"
    • Some steps in the algorithm may be common to all implementations so that code can be in the superclass
    • Code Outline which shows a simple templated method
    • Example Code for coffee or tea brewing; diagram of this example
    • Swing use of a template method in JFrame: the update() method of JFrame (not shown) calls paint().

    Design Principle: the Hollywood Principle

    Don't call us, we'll call you
    Translation:
    • Context: a high-level component is talking to a low-level component.
    • It is the high-level component that is in control of the process
    • High-level component has a (general) interface for invoking low-level component
    • Low-level component services the high-level component via the calls
    Template method uses this pattern:
    • The superclass with the template in it is the high-level component
    • The subclasses are the low-level components
    • The super is in control and calls the subs

    Iterator

    HFDPs Chapter 9

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

    • The Java Collections Framework includes an Iterator interface w/methods next(), hasNext(), remove() (remove removes the current method from the underlying collection).
    • Iterators need to hold the state of progress of one iteration through the object, so you make a new one of these guys for each iteration through the collection.
    • The take-home message here is not only can you use iterators on Java collections, implement Iterator on your own classes which you may need to iterate through.

    Composite

    HFDPs Chapter 9

    Go4 description: Compose objects into tree structures to represent part-whole hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly.
    Diagram

    • This is a very basic and critical pattern we already ran into several times.
    • If you needed a recursive union type in C, use the composite pattern in an OO language.
    • This is another way switch statements get done away with: a switch on such a union amounts to dynamic class lookup which is bad; instead, ask the objects to do the task their way.
    • Example: Component/Container of Swing.
      • Swing implements a variation on Composite: JComponent is a subclass of Container, and so JComponent and all its subclasses (i.e., all Swing widgets) are themselves containers.
      • Also, paint in a JComponent container first paints the component (paintComponent) and then all of its children, if any (paintChildren()).
    Check out the following code which illustrates how JButton is a Container: we add a tree component to a JButton "composite/container":
    import javax.swing.*;
    import javax.swing.event.*;
    import java.awt.*;
    
    public class TreeOnButton {
      public static void main(String[] args) {
        JButton button = new JButton();
        button.setLayout(new BorderLayout());
    
        final JLabel buttonText = new JLabel("Press me",
          JLabel.CENTER);
        button.add(buttonText, BorderLayout.NORTH);
    
        JTree tree = new JTree();
        tree.addTreeSelectionListener(new TreeSelectionListener() {
          public void valueChanged(TreeSelectionEvent e) {
            buttonText.setText("Press for " +
              e.getPath().getLastPathComponent());
          }
        });
        button.add(tree, BorderLayout.CENTER);
    
        JFrame f = new JFrame("Tree on Button");
        f.getContentPane().setLayout(new FlowLayout());
        f.getContentPane().add(button);
        f.setSize(500,500);
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.show();
      }
    }
    
    Run it and watch some strange stuff ("don't do this at home").

    State

    HFDPs Chapter 10

    Go4 description: Allow an object to alter its behaviour when its internal state changes. The object will appear to change its class.
    Diagram

  • An Example from the book is a gumball machine
  • Another Example: in Fowler Refactoring Chapter 1, the state pattern was used for movies because their classification could dynamically change (a new class Price was introduced which is the State above, and it had subclasses RegularPrice, etc; Movie is the Context above, holding the Price state).
  • State is great: the underlying states of the design now have clear reifications as class names.

    Strategy

    (This pattern was touched on in HFDPs Chapter 1)

    Go4 description: Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it.

    • This is nearly identical to State, it supports dynamic changing of some aspects of a class--in this case an algorithm.
    • The class diagram and sequence diagram are identical as well.
    • In fact it is arguably the same pattern: the algorithm is in fact varying because of some underlying state change.

    Proxy

    HFDPs Chapter 11

    Go4 description: Provide a surrogate or placeholder for another object to control access to it.
    Diagram

    Proxies are one of the most useful advanced patterns, with many purposes

    • Virtual Proxy: maybe actual object hasn't been created or loaded yet (lazy creation),
    • Java RMI uses remote proxies for remote objects, and Hibernate uses proxies for database-stored objects.
    • Proxies can be used for access control: e.g. ReadOnlyGoober could be a proxy class for a Goober which does not forward setter messages to Goober, but forwards all others.
    • Java also has a Proxy class
      • It is a variant on the proxy pattern: there may not even be a real subject!
      • The handling of messages can also be dynamically decided - it is a dynamic proxy.
      • Here is a code example of how the Java Proxy class is used.
    • Proxy is the special case of adaptation/delegation where nearly all methods of one object delegate to the same method in another object.

    More Gang of Four Patterns

    The patterns from here on down are not as central as the ones above and for most of them we will not cover them in as much depth. They are summarized in Chaper 14 of the HFDPs book.

    Bridge

    HFDPs p.612

    Go4 description: Decouple an abstraction from its implementation so that the two can vary independently.
    Go4 Diagram:

    Bridge is an apt name, because it forms a bridge between two inheritance heirarchies.

    Bridge emerges as a result of a refactoring which introduces delegation:

    • Suppose you have a complex inheritance tree (example: Window with subclasses IconWindow, Dialog, etc)
    • Suppose there are also different modalities of implementation of the whole tree, for example PC, UNIX, and Mac-specialized implementations are needed.
    • Refactor this mess into two trees, an abstraction hierarchy which is the original Window/IconWindow/Dialog tree with the PC/Mac/UNIX implementation bits removed, and an implementation tree which has an abstract Implementation class and each concrete ImplementationMac, ImplementationPC, ImplementationUNIX as subclasses.
    • The abstract tree then delegates to its implementation object for the low-level code.

    Builder

    HFDPs p.614

    Go4 description: Separate the construction of a complex object from its representation so that the same construction process can create different representations.
    Go4 Diagram:

    • the Director needs to create many different kinds of parts to make the full product. In the stupid method, the director has a whole pile of different classes he has to new.
    • In the smart method above, he has a uniform pile (an Array say) of Builder's, and by invoking BuildPart() on each one in a loop, he gets all his parts made with minimal code fuss.
    • the ConcreteBuilder is a particular concrete Builder, a factory class, designed to create Product's.

    Chain of Responsibility

    HFDPs p.616

    Go4 description: Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle a request. Chain the receiving objects and pass the request along the chain until an object handles it.
    Go4 Diagram:

    • This pattern shares some structural similarities with Java exceptions: either they are handled or passed on. But with exceptions they are implicitly passed on if they are not handled; here the passing on is explicit
    • This pattern is useful for hierarchical structures where a request can be handled at multiple layers.
    • Example: GUI event handling can be done hierarchically. If a contained view doesn't want to handle an event it can delegate it to its container, etc up the chain to the window. Java doesn't use this event model however.

    Flyweight

    HFDPs p.618

    Go4 description: Use sharing to support large numbers of fine-grained objects efficiently.
    Go4 Diagram:

    • This is a more specialized pattern, not used as often as the others.
    • Example: Suppose a card game required 50 decks of cards. Only one set of actual cards need be created and shared

    Interpreter

    HFDPs p. 620

    Go4 description: Given a language, define a representation for its grammar along with an interpreter that uses the representation to interpret sentences in the language.
    Go4 Diagram:

    • This is a very specific pattern to represent language syntax.
    • It is a variation on Composite. In general it is also like Composite showing how union types are encoded via this recursive diagram structure.

    Mediator

    HFDPs p. 622 Put someone in charge (a mediator) of an interaction between two classes.

    Go4 description: Define an object that encapsulates how a set of objects interact. Mediator promotes loose coupling by keeping objects from referring to each other explicitly, and it lets one vary their interaction independently.
    Go4 Diagram:

    Memento

    HFDPs p. 624

    Go4 description: Without violating encapsulation, capture and externalise an object's internal state so that the object can be restored to this state later.
    Go4 Diagram:

    • This pattern can be one useful way to interact with a database in an object-oriented fashion: keep mementos around of all objects

    Prototype

    HFDPs p. 626

    Go4 description: Specify the kinds of objects to create using a prototypic instance and create new objects by copying this prototype.
    Go4 Diagram:

    • When you want new objects, copy from a prototype instead of creating directly from a class.
    • Useful when its a significant effort to create object structure from scratch.
    • Example: To create a new Deck of 52 playing cards, cards could be copied from a static variable in Deck which was originally initialized when the class was loaded to hold a "fresh" Deck of Card objects rather than making cards all over again.

    Visitor

    HFDPs p. 628

    Go4 description: Represent an operation to be performed on the elements of an object structure. Visitor allows one to define a new operation without changing the classes of the elements on which it operates.
    Go4 Diagram:

    • Suppose you have an Element object in a variable and need to perform a switch on what concrete subclass of Element we in fact have.
    • Note that this is an incredibly common C programming pattern on union types -- you are casing on which branch of the union you are in (the C analogy of inheritence is union).
    • The problem is this notion does not fit well with O-O, the union is treated as passive in this switch; you are also casing at run-time on what class an object is, a brittle programming pattern.
    • Alternative 1: add a method to each class in the union to do the walkthrough
      • Big Advantage: we kept things highly O-O!
      • Big Disadvantage: this is shotgun surgery -- each time we want to do such a switch we have to add a method to all the classes in the tree. Code gets all spread out.
    • Alternative 2: Visitor
      • Add an intermediary class, the visitor, which holds all the cases
      • The classes in the original inheritance hierarchy gets a new method Accept to help "walk" the visitor through the union
      • ... this is a compromise, we are not completely violating O-O and we avoid shotgun surgery when adding an operation over the tree, but it adds complexity to the design.
      • Note that if we add a new concrete element type we on the other hand have to do surgery on all visitors. But, we have localized the surgery to just the visitors.
    • This pattern is another pattern that is useful to get rid of switch statements.
    How the Visitor works:
    • Abstract superclass Visitor is the superclass of all visitors
    • ConcreteVisitor1 is a concrete visitor (e.g. we make a class GetHealthRatingfor getHealthRating() in the menu example); we make new ConcreteVisitorX for each different switch we wanted to do over the union.
    • ConcreteVisitor1 has a method visitConcreteElementA etc for each kind of node A/B/.. in the original union structure - this is where the code in the original switch for the case it is A/B.. goes.
    • anElement.accept(aVisitor) starts the visiting process
    • This method in each inheritence class ConcreteElementA etc in turn calls the correct "case" to be performed on it, e.g. ConcreteElementA calls visitConcreteElementA(this) which will run the correct case of the switch..
    The place where Visitor really shines is using it together with Composite to visit a tree structure.