Week 4. Design Patterns

 

Design Pattern

A design pattern is a description of a recurring solution for a recurring design problem in a certain context.

Link to original

Design patterns are good for:

  1. Learning good design through direct application of patterns and by better understanding how patterns use the various tools of object orientation.
  2. Language of design, names for complex design structures. Better communication in teams and understanding of ideas. “We can use a singleton here, I can see an observer here.”
  3. Improving existing designs.

GoF Design Patterns

Gangs of Four Design Patterns

There are 23 listed design patterns in GoF:

  1. Strategy: Defines a family of algorithms, encapsulates each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients who use it.
  2. Decorator: Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality.
  3. Factory Method: Define an interface for creating an object, but let the subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.
  4. Observer: Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.
  5. Chain of Responsibility: Avoid coupling the sender of a request to its receiver by giving more then one object a chance to handle the request. Chain the receiving objects and pass the request along the chain until an object handles it.
  6. Singleton: Ensure a class only has one instance and provide a global point of access to it.
  7. Flyweight: Use sharing to support large numbers of fine-grained objects efficiently. A flyweight is a shared object that can be used in multiple contexts simultaneously. The flyweight acts as an independent object in each context; it’s indistinguishable from an instance of the object that’s not shared.
  8. Adaptor: Convert the interface of a class into another interface clients expect. Adaptor lets classes work together that couldn’t otherwise because of incompatibility interfaces.
  9. Facade: Provide a unified interface to a set of interfaces in a system. Facade defines a higher-level interface that makes the subsystem easier to use.
  10. Template: Define a skeleton of an algorithm in an operation, deferring some steps to sub-classes. Template Method lets sub-classes redefine certain steps of an algorithm without changing the algorithms’ structure.
  11. Builder: Separate the construction of a complex object from its representation so that the same construction processes can create different representations.
  12. Iterator: Provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation.
  13. Composite: Compose objects into tree structures to represent part-whole hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly.
  14. Command: Encapsulate a request as an object, thereby letting you parameterise clients with different requests, queue or log requests, and support: un-doable operations.
  15. Mediator: 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 lets you vary their interaction independently.
  16. State: Allow an object to alter its behaviour when its internal state changes. The object will appear to change its class.
  17. Proxy: Provide a surrogate or placeholder for another object to control access to it.
  18. Abstract Factory: Provide an interface for creating families of related or dependent objects without specifying their concrete classes.
  19. Bridge: Decouple an abstraction from its implementation so that the two can vary independently.
  20. Interpreter: Given a language, define a representation for its grammar along with an interpreter that uses the representation to interpret sentences in the language.
  21. Memento: Without violating encapsulation, capture and externalise an object’s internal state so that the object can be restored to this state later.
  22. Prototype: Specify the kinds of objects to create using a prototypical instance and create new objects by copying this prototype.
  23. Visitor: Represent an operation to be performed on the elements of an object structure. Visitor lets you define a new operation without changing the classes of the elements on which it operates.
Link to original

Variability patterns

Sometimes behaviour needs to vary for different use cases, we can use:

  1. Template Method
  2. Strategy
  3. State (not discussed)
  4. Bridge
  5. Visitor (not discussed)

Template Method

Template Method (Design Pattern)

A template method contains a generic algorithm skeleton, delegates part of algorithm to abstract hook methods.

  • Hook methods are overwritten in sub-classes to provide the specifics steps.
  • Template and hook method are implemented in the same class / object.
  • Polymorphism enables variability.

Benefits:

  • Easily separate core logic and variation points.
  • Easily provide many different implementations of the details and use the one you need.

Disadvantages:

  • Variation is only supported at compile time.
Link to original

Strategy (“Template Class”)

Strategy (Design Pattern)

A strategy is a “template class”, where a template method contains the generic algorithm skeleton.

  • Parts of an algorithm are delegated to a strategy, hook methods are overwritten in sub-classes of strategy to provide the specific steps.
  • Template and hook methods are implemented in different classes and objects so can be exchanged at runtime.
  • Also uses polymorphism to enable variability.

Benefits:

  • Easily separate core logic and variation points.
  • Easily provide many different implementations of the details and use the one you need.
  • Functionality can be varied at runtime.

Disadvantages:

  • Object Schizophrenia

    Needs an additional object leading to “Object Schizophrenia

    • this in the strategy object refers to the strategy object but outside clients only know about the context. Meanwhile in the context object, this means the context object.
    Link to original
  • Can’t easily vary multiple aspects of the algorithm.
Link to original

Bridge

Bridge (Design Pattern)

A bridge is a “facet class” which provides access to other functionality. We may need to use a bridge where the behaviour of and object needs to change, possibly dynamically, but there are multiple orthogonal dimensions of variability.

Benefits:

  • Easily separate core logic and variation points
  • Easily provide many different implementations of the details and use the one you need
  • Support orthogonality varying different aspects of the behaviour – facets
  • Functionality can be varied at system runtime

Disadvantages:

  • Object Schizophrenia
  • Fairly complex set up with logic distributed over several classes.
  • Requires selecting correct classes to init and wire up.
Link to original

Example: Applying Variability Patterns

Algorithm for averaging a data-set:

  1. Load data-set
  2. Run through all items and determine count and sum
  3. Compute average

Template Method

Strategy

Bridge

Creational Patterns

There’s a number of different things we may want to control during creation of objects:

  1. Controlling what class to instantiate
    • Factory Method
    • Abstract Factory
  2. Making sure objects are configured correctly
    • Builder
    • Prototype (not discussed)
  3. Controlling number of objects
    • Singleton (not discussed)

Factory Method

Factory Method (Design Pattern)

Thinking about the Bridge (Design Pattern), we may have a situation where there are dependencies between different facets.

Benefits:

  • Allows polymorphic creation of objects
  • Nicely encapsulates object creation - client depends on abstraction not implementation

Disadvantages:

  • Creation is bound to a containing class which contains other functionality
  • Switching creation independently of other functionality is impossible
Link to original

Abstract Factory

Abstract Factory (Design Pattern)

This is a natural progression of the Factory Method (Design Pattern) but applies the Strategy (Design Pattern).

Benefits:

  • Allows polymorphic creation of objects
  • Nicely encapsulates object creation – client depends on abstraction not implementation
  • Groups creation logic for multiple inter-dependent classes
  • Change one line of code to replace a set of classes or even switch dynamically at runtime

Disadvantages:

  • Slightly more indirect code
  • Only covers creation
Link to original

Builder

Builder (Design Pattern)

The builder configures and assembles objects, and can ensure domain constraints are always satisfied and only valid complex object configurations are returned.

We can combine this with the Factory Method (Design Pattern) or Abstract Factory (Design Pattern) in order to allow varying the specific type of complex object.

Benefits:

  • Encapsulates object configuration + constraints are encapsulated and located in a single place
  • Groups construction logic for multiple inter-dependent classes

Disadvantages:

  • More indirect code
  • Needs to be combined with factory pattern to enable easy switching of concrete implementations.
Link to original

Extensibility Patterns

  1. Composite (+ Visitor)
  2. Object Recursion
  3. 1-Object Recursion
  4. Chain of Responsibility
  5. Decorator
  6. Adapter
  7. Observer (not discussed)
  8. Proxy (not discussed)

Composite

Composite (Design Pattern)

Composition involves using recursion to abstract the super-class. It means everything can be treated as a Component and recursion over the structure is “elegantly” handled by polymorphism.

Benefits:

  • Can be used to represent a tree data structure.
  • Allows treating complex and simple objects in homogeneous manner.

Disadvantages:

  • Assumes there is a reasonably homogeneous interface for all elements of the graph, we don’t know where exactly to put operations for adding, removing and accessing children.
  • Recursive operations are distributed all over the class hierarchy. Can make understanding more difficult and can make extension more difficult.
Link to original

Visitor

Visitor (Design Pattern)

Building on Composite (Design Pattern), we want to add flexible operations to the data structure. We can implement the visitor pattern where the method to call is decided by looking at the run-time class of Element and at the run-time class of Visitor. Client can still call the method without knowing the sub-type of Element.

Benefits:

  • Easily add new operations to an existing class hierarchy without changing the classes themselves.
  • Particularly good when the classes come from a library, so cannot be changed.
  • Keep all functionality for one purpose in one place.
  • Rather than scattered across the structural classes separate structure and behaviour.

Disadvantages:

  • Separates structure and behaviour.
  • Adding new class means finding and changing all visitors.
Link to original

Object Recursion

Object Recursion (Design Pattern)

We use recursion to abstract the super-class, which means everything can be treated as an Object and recursion over the structure is handled by polymorphism.

could be or .

Link to original

Chain of Responsibility

Chain of Responsibility (Design Pattern)

We use 1-recursion to abstract the handler class:

  • Every handler can have 1 follower.
  • Handlers attempt to handle request one at a time.
  • Once handled, we can decide to hand to next handler or stop.
  • Can adjust chain dynamically.

Benefits:

  • Each case nicely encapsulated
    • Can be treated in isolation
    • More compact view of all cases possible
  • Possibility to dynamically adjust request handling as needed
  • Can be declaratively configured
    • This is what many modern web frameworks do for route management

Disadvantages:

  • Object structure must be set up explicitly with objects constructed at run time
  • Object proliferation
Link to original

Decorator

Decorator (Design Pattern)

A decorator is a specialised chain allowing to add behaviour to an object after the fact, for example scrolling added to an arbitrary widget.

Link to original

Adapter

Adapter (Design Pattern)

An adapter adjusts the interface of an object to a new usage context.

Link to original