William Liu

Design Patterns

Summary

The below notes are based on the book Designs Patterns - Elements of Reusable Object-Oriented Software. The idea is that all well structured object-oriented architectures are full of patterns. When you design a system, you need to focus on the common collaborations among its objects (i.e. its design pattern). The book shows you the importance that patterns can play in architecting complex systems, then it provides a reference of well-engineered patterns that a developer can apply to their own applications.

Why Design Patterns

Designing reusable object-oriented software is hard. Recording the experience of designing object-oriented software is known as design patterns, that way we can reuse successful designs and architectures. For a more specific definition, we’ll say that design patterns are descriptions of communicating objects and classes that are customized to solve a general design problem in a particular context. Each design pattern identifies participating classes and instances, their roles and collaborations, and the distribution of responsibilities.

Besides software engineering, you can see design patterns used. Playwrights don’t write plots from scratch. They normally follow a pattern like the ‘Tragically Flawed Hero’ or ‘The Romantic Novel’. Once you know the patterns, a lot of design decisions follow automatically.

Essential Elements of a Design Pattern

A design pattern has four essential elements:

Types of Design Patterns

Design Patterns fall into three categories.

The above categories are classified based off two criteria:

1.) Purpose reflects what a pattern does

Creational patterns are about object creation. Structural patterns deal with the composition of classes or objects Behavorial patterns characterize the ways in which classes or objects interact and distribute responsibility

2.) Scope specifies whether the pattern applies primarily to classes or to objects. For Python, everything is an object while classes are essentially a template to create your objects

Class patterns deal with relationships between classes and their subclasses; these relationships are established through inheritance, so they are static - fixed at compile-time.

Object patterns deal with object relationships, which can be changed at run-time and are more dynamic.

Almost all patterns use inheritance so some extent so the only patterns labeled ‘class patterns’ are those that focus on class relationships. Most patterns are thus labeled in the Object patterns.

                 -------------------------------------------------------------------
                 |                        Purpose                                  |
                 -------------------------------------------------------------------
                 |   Creational      |   Structural      |   Behaviroal            |
------------------------------------------------------------------------------------
|Scope |  Class  |   Factory Method  |  Adapter (class)  | Interpreter             |
|      |         |                   |                   | Template Method         |
|      -----------------------------------------------------------------------------
|      |  Object |  Abstract Factory |  Adapter (object) | Chain of Responsibility |
|      |         |  Builder          |  Bridge           | Command                 |
|      |         |  Prototype        |  Composite        | Iterator                |
|      |         |  Singleton        |  Decorator        | Mediator                |
|      |         |                   |  Facade           | Memento                 |
|      |         |                   |  Flyweight        | Observer                |
|      |         |                   |  Proxy            | State                   |
|      |         |                   |                   | Strategy                |
|      |         |                   |                   | Visitor                 |
----------------------------------------------------------------------------------

Catalog of Design Patterns

Common design patterns include:

Simplest and Most Common Patterns:

Out of the above, the simplest and most common patterns are:

Additional Patterns

Creational Patterns

Creational design patterns abstract the instantiation process. Why is this important? As systems evolve, they depend more on object composition than class inheritance. That means when you crate objects with specific behaviors, it requires more than just instantiating a class.

Maze Example

The below five creational patterns are closely related; let’s see what this means for building a maze for a computer game. Let’s assume a maze is a set of rooms and the maze knows its neighbors, which are represented by classes for Room, Door, and Wall (which has a common abstract class of MapSite).

So let’s say we define a MazeGame class that creates the maze. We can setup this maze a few ways:

But what if we need to create an EnchantedMaze class that has new components like DoorNeedingSpell or when we call the funciton to add a room, we now need an EnchantedRoom instead of a regular Room? The creational patterns provide different ways to remove explicit references to concrete classes from code that needs to instantiate them.

Abstract Factory (aka Kit)

Intent: Provide an interface for creating families of related or dependent objects without specifying their concrete classes

Motivation: An example of an Abstract Factory would be a user interface toolkit that supports multiple look =-and-feel standards. You can define different look-and-feel appearances and behaviors for various user interface ‘widgets’ like scroll bars, windows, and buttons.

Solution: We define an abstract WidgetFactory class that declares an interface for creating each basic kind of widget. We have an abstract class for each kind of widget and concrete subclasses implement widgets for specific look-and-feel standards. The WidgetFactory interface has an operation that returns a new widget object for each abstract widget class. Clients call these operations to obtain widget instances (but clients aren’t aware o the concrete classes they’re using, meaning that clients stay independent of the look and feel).

      So what does this do? Clients create widgets through the WidgetFactory interface. There is no knowledge
      of the classes that implement widgets for a particular look and feel. The clients only have to commit
      to an interface defined by an abstract class (not a particular concrete class).

Application: Use the AbstractFactory pattern when:

Partipants:

Consequences: The Abstract Factory pattern has the following benefits and liabilities:

  1. It isolates concrete classes. The Abstract Factory pattern helps you control the classes of objects that an application creates. A factory encapsulates the responsibility and the process of creating product objects so it isolates clients from implementation classes. Clients manipulate instances through their abstract interfaces.
  2. It makes exchanging product families easy. The class of a concrete factory appears only once (at instantiation). It makes it easy to change the concrete factory an application usses. You can change different product configurations simply by changing the concrete factory.

    Builder

Factory Method

Prototype

Singleton

Structural Patterns

Structural Patterns are concerned with how classes and objects are composed to form larger structures. Structural class patterns use inheritance to compose interfaces or implementations.

Think how multiple inheritance mixes two or more classes into one. The result is a class that combines the properties of its parent class. This might be useful for making independently developed class libraries to work together. An example code snippet in Python might look like:

class Subclass(BaseClass1, BaseClass2, BaseClass3, ...):
    pass

Another example is the Adapter class. The adapter makes one interface (the adaptee’s) conform to another, providing a uniform abstraction of different interfaces. Say you have “x” and you need “y”, then Adapter solves this problem. There are a number of ways to accomplish it, but here is the general idea with an example code snippet:

class WhatIHave:
    def g(self):    pass
    def h(self):    pass

class WhatIWant:
    def f(self):    pass

class MyAdapter(WhatIwant):
    def __init__(self, whatIHave):
        self.whatIHave = whatIHave

    def f(self):
        # implement behavior using methods in WhatIHave
        self.whatIHave.g()
        self.whatIHave.h()

class WhatIUse:
    def op(self, whatIWant):
        whatIWant.f()

Another example of a structural pattern is Facade, where we apply the rule “If something is ugly, hide it inside an object”. Usually this is implemented as a singleton abstract factory. You might use this if you have a confusing collection of classes and interactions that the client programmer doesn’t really need to see, you can create an interface that is useful for the client programmer and that only presents what’s necessary.

Behavioral Patterns

Architectual Patterns and Styles

The above ‘Creational’, ‘Structural’, ‘Behaviorla’ are design patterns from ‘The Gang of Four’. There’s also a few architectural patterns and styles:

Architectural Patterns

Architectural Styles

Example Design Pattern - the MVC

In web applications, we commonly see the Model/View/Controller (MVC) classes used to build user interfaces. We have:

MVC - Models and Views with Observer Pattern

MVC allows decoupling of views and models by establishing a subscribe/notify protocol between them. A view must ensure that its appearance reflects the state of the model. When a model’s data changes, the model notifies views that depend on it. In response, each view gets an opportunity to update itself. You can then attach multiple views to a model to provider different presentations.

We can see this as a general pattern of decoupling views from models, or we can see this apply to a more general problem: decoupling objects so that changes to one can affect any numer of others without requiring the changed object to know details of the others, which is known as the Observer Pattern.

MVC - Views with Composite Pattern

MVC allows views to be nested. An example might be a control panel of buttons that appears as a view containing a lot of button views. We can think of this user interface that groups together multiple views as a Composite View, basically treating the Composite View like one of the nested Views. This general design pattern is the Composite Pattern, which lets you create a class hierarchy where some subclasses define primitive objects (e.g. Button) and other classes define composite objects (e.g. Composite View) that assembles the primitives into more complex objects. Basically, a group of objects is treated the same way as a single instance of the same type of object.

MVC - Views and Controllers with Strategy Pattern

MVC’s View uses an instance of the Controller subclass to implement a particular response strategy; to implement a different strategy, you just replace the instance with a different kind of controller. For example, a view can be disabled so that it doesn’t accept any input by giving it a controller that ignores input events.

MVC has a View-Controller relationship that is an example of the Strategy Pattern. A Strategy is an object that represents an algorithm. It’s useful when you want to replace the algorithm either statically or dynamically, when there’s a lot of variants of the algorithm, or when the algorithm has complex data structures you want to encapsulate.

MVC - Views with Decorator Pattern

MVC might have a View that needs the scrolling functionality. We can use the Decorator Pattern to add scrolling capabilities to a single object.

MVC - Controllers with Factory Pattern

MVC might have a Controller that specifies the default Controller Class for a View. We can use the Factory Pattern to create objects without having to specify the exact class of the object that will be created.

How to Select a Design Pattern

Here’s several approaches to finding the design pattern that’s right for your problem:

Object-Oriented Programs

So let’s take a step back and look at what makes up object-oriented programs: objects.

What makes up an object?

An object has both data and the procedures that operate on that data.

Calling Procedures with Requests

Requests are the only way to get an object to execute an operation. Operations are the only way to change an object’s internal state. Because of these restrictions, the object’s internal state is said to encapsulated; it cannot be accessed directly and its representation is invisible from outside the project.

Object-oriented design methodologies

You can do object-oriented design a few different ways:

Object Interfaces

Objects are known only through their interfaces. So what’s an interface?

Every operation declared by an object specifies the operation’s name, the objects it takes as parameters, and the operation’s return value (aka the operator’s signature). The set of all signatures defined by an object’s operations is called the interface to the object.

A type is a name used to denote a particular interface. An object can have many types. A type is a subtype of another if its interface contains the interface of its supertype. Often you’ll hear of a subtype inheriting the interface of its supertype

Diving into Common Patterns

Adapter (Class, Object Structural)

Adapter (aka __wrapper) lets classes work together that normally couldn’t because of incompatible interfaces.

Design Principles

Here’s a list of design principles, which all you to ask questions about your proposed design.