Design Patterns - egnomerator/misc GitHub Wiki

Sources

Pluralsight Course TOC: https://app.pluralsight.com/library/courses/patterns-library/table-of-contents

  • this is the main reference/source for the content in this article

Design Patterns: Elements of Reusable Object-Oriented Software

A few other quick references to mainly GoF design patterns

Intro

Design patterns are

  • General and reusable solutions to common problems in software design
  • A template or recipe for solving certain problems
  • named solutions (each design pattern has a name)
  • About application and system design (both high level and low level)
  • Abstractions over code
  • Deal with relationships between classes or "collaborators"
  • Deal with problems that have already been solved
  • Not concerned with specific implementations
  • Not a finished solution
  • Not an algorithm

History

Design patterns in programming were originally inspired by a building architect

  • Christopher Alexander
  • his book A Pattern Language: Towns, Buildings, Construction 1977

Kent Beck and Ward Cunningham presented patterns at OOPSLA in 1987

Design Patterns: Elements of Reusable Object-Oriented Software, 1994

  • by the Gang of 4: Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides

Pattern Language

Classification criteria (from the GoF book)

  1. Purpose (e.g. creational, structural, behavioral)
  2. Scope: pattern applies primarily to classes or to objects (most patterns are in the object scope)

Categories or Classifications of Named Design Patterns

  • Creational: abstract the instantiation process
    • main concepts:
      • encapsulate knowledge about which concrete classes the system uses
      • hide how instances of these concrete classes are created and combined
    • creational object patterns defer object creation to another object
    • creational class patterns: defer object creation to a subclasses
  • Structural: the composition of classes or objects to form larger structures
    • structural object patterns: describe ways to compose objects to realize a new functionality
    • structural class patterns: use inheritance to compose interfaces or implementations
  • Behavioral: concerned with algorithms, assignment of responsibility between objects, and communication between objects and classes
    • help to shift focus away from the flow of control and onto the way objects are interconnected
    • behavioral object patterns: use object composition rather than inheritance
    • behavioral class patterns: use inheritance to distribute behavior between classes
  • Concurrency
  • Security
  • Sql
  • User Interface
  • Relational
  • Social
  • Distributed

Software engineering professional shared language

  • helps avoid re-inventing constantly
  • provide a starting-point for a solution
  • increase production speed of a team
  • generally improve system and application design

Criticisms of Design Patterns

  • unnecessary duplication
  • weak points in languages
  • same as other abstractions

Adapter

Non-Pluralsight notes

Other common names for this Pattern: "Wrapper" and "Translator"

Github

Category: Structural

note: there is an Object Adapter and a Class Adapter pattern

  • object adapter is the composition version
  • class adapter is the inheritance version
  • more details are in the geeksforgeeks source below

Non-Pluralsight sources:

UML

uml design pattern adapter

Roles

  • Client: needs access to functionality in a class (Adaptee) that it can't work with
    • expects to use a certain interface (the Target)
    • needs functionality encapsulated by a class (Adaptee) that doesn't implement the Target interface
  • Target: the interface the Client expects to use
  • Adapter: "converts" the Adaptee's interface to the Target interface
    • implements the Target interface
    • holds a reference to the Adaptee
    • when client calls Target interface, Adapter internally calls Adaptee's functionality
  • Adaptee: the class/interface that encapsulates the functionality the Client needs

Similar patterns

  • Decorator: dynamically adds responsibility to the interface by wrapping the original code
  • Delegation: support "composition over inheritance"
  • Facade: provides a simplified interface

Pluralsight notes

Motivating Examples

  • a class that would be useful to your application does not implement the interface you require
    • so you need an adaptor to sit between the class you need and the interface you require
  • you are designing a class or framework and you want to ensure it is usable by a wide variety of as-yet-unwritten classes and applications
    • so you could support an adaptor that can make it easier for future clients to use your code

Adapters are also commonly known as Wrappers.

This module discusses only object adapters which do not require multiple inheritance (as class adapters do).

Intent

  • convert the interface of one class into another interface that the client expects
  • allow classes to work together that otherwise couldn't due to incompatible interfaces
  • future-proof client implementations by having them depend on adapter interfaces rather than concrete classes directly

Applicability

  • Use the adapter pattern when ...
  • you want to use an existing class with an interface that doesn't match what you require
  • you want to create a reusable class that cooperates with unrelated or unforeseen classes
    • (classes that won't necessarily share the same interface)

How it gets used

  • clients depend on the adapter interface, rather than a particular implementation
  • at least one concrete adapter class is created to allow the client to work with a particular class that it requires
  • future client needs for alternate implementations can be satisfied through the creation of additional concrete adapter classes
  • effective way to achieve OCP

Collaboration

  • clients call operations on an adapter instance
  • adapter instance calls adaptee operations that carry out the request

Consequences

  • a single adapter interface may work with many adaptees
    • e.g. one adaptee and all of its subclasses, or
    • e.g. separate adaptees via separate concrete adapter implementations
  • can be difficult to override adaptee behavior (with object adapter)
    • must subclass adaptee and add overridden behavior
    • then change concrete adapter implementation to refer to adaptee subclass

Related patterns

  • Repository
    • a very common use of the Adapter pattern
  • Strategy
    • adapter is often passed into a class that depends on it, thus implementing the strategy pattern
  • Facade
    • also a wrapper (like the adapter is); Facade often wraps many classes
    • Facade attempts to simplify the wrapped interface (or API)

Bridge

Non-Pluralsight notes

Github

Category: Structural

Non-Pluralsight sources:

UML

uml design pattern bridge

Pluralsight notes

What is it

  • it "decouples an abstraction from its implementation so the two can vary independently" - GoF

Roles

  • Abstraction (Manuscript)
    • abstraction over the main concept (something we want to read/print)
    • uses an Implementor
  • Refined Abstractions (FAQ, Book, etc.)
    • implement Abstraction interface
  • Implementor (IFormatter)
    • in order to read/print a reading Manuscript, we need some kind of formatting
  • Concrete Implementor (e.g. StandardFormatter, BackwardsFormatter, FancyFormatter)

Pattern enabled us to

  • use combinations of Refined Abstractions and Concrete Implementers
  • reduce class hierarchies

When to use

  • Basically any time you need to create a large class hierarchy (doesn't necessarily have to be "large"), and have to split classes based on abstractions and implementations
  • examples
    • support multiple UI functionality based on operating system
    • multiple persistence implementations
    • .NET Provider Model

Builder

Non-Pluralsight notes

Github

Category: Creational

Non-Pluralsight sources:

UML

uml design pattern builder

Pluralsight notes

Overview

Separates the construction of a complex object from its representation so that the same construction process can create different representations - GoF

  • i.e. separate data from logic, reuse the logic
  • an analogy - getting a sandwich from subway
    • a series of questions and answers as they build the sandwich with the ingredients per question
    • enter builder pattern: i just enter the store with a list of ingredients for them, they just follow their steps using the given ingredients

Refactoring to the Builder pattern

  • Problem 1: too many parameters
    • had a constructor with a long list of parameters
    • improved readability by
      • converting private props to public auto props
      • newing up object and setting props rather than passing long list of ctor params
    • this improvement introduced problems
      • with this approach we might fail to set some values that we need to set
      • no way to control the order of the creation of the sandwich
    • so we might actually be worse off now in spite of improving readability
  • Problem 2: order dependent
    • now our problem is that our sandwich creation depends on
      • manually ensuring that we set all these properties
      • what order we do things while creating the sandwich
        • we can set different parts of the sandwich at any time
    • create a MySandwichBuilder class to encapsulate creation of sandwich and setting of properties
      • added some methods for "build" steps so we see some logical list of steps rather than just a bunch of property assignments
    • improved by
      • encapsulating build logic into separate class for reusability, consistency
      • ensuring sandwich is always built property
      • cleaning up client code so that building a sandwich is just a couple readable lines
    • problems
      • no standard definition of how to create a sandwich
      • still no way to create different sandwiches
        • we don't want to copy/paste code to a new sandwich type
  • Problem 3: different constructions
    • right now we can't support safely creating multiple types of sandwich
    • create a SandwichBuilder class
      • this class will provide a common interface to force subclasses
        • to have all required info for each step (ingredients)
    • create a SandwichMaker class
      • this class will provide a common interface to force subclasses
        • to follow same steps
        • to use a SandwichBuilder for the ingredients

Roles

  • a Director (sandwich maker class)
    • which contains logic (order of sandwich creation steps)
    • and which uses a builder interface
    • and which is used by client code directly
  • a Builder (base sandwich Builder class)
    • which ensures all builder implementations follow a common interface
    • and which holds the instance of the products (sandwich)
  • Concrete Builders (Builder implementations - types of sandwich builders)
    • which provide the data (ingredients) used by the Director logic
    • should have more than one of these
      • purpose of this pattern is to enable the consistent use of different kinds of these
  • Product
    • what we are building
    • it's one type--a point of the builder pattern is to provide different ways to build one type
      • one type; different data results
      • rather than a hierarchy of product types--this is not the purpose of the builder pattern
    • this should be a fairly complex object
      • a point of the Builder pattern is to bring stability and safe versatility to how we create a complex object
      • if we are trying to create a simple object, the builder pattern probably introduces more complexity than it's worth

Variations (not the builder pattern)

  • StringBuilder
    • this is technically not the builder pattern
    • it doesn't work with all the elements of the builder pattern
    • it doesn't enforce a process for how the string is built
  • Fluent API
    • var thing = new Thing().WithThis().WithThat().Create();
    • it doesn't work with all the elements of the builder pattern
    • it doesn't enforce a process for how the Thing is built

Chain of Responsibility

Non-Pluralsight notes

Github

Category: Behavioral

Non-Pluralsight sources:

UML

uml design pattern chain of responsibility

Pluralsight notes

Purpose

  • Provides a simple mechanism to decouple the sender of a message from the receiver

What is it

  • An ordered list of message handlers that know how to do 2 things:
    1. Process a specific type of message
    2. pass the message to the next handler in the chain

Traits

  • the sender is only aware of the first receiver in the chain of receivers
  • each receiver is only aware of the next receiver
  • receivers process the message or send it down the chain
  • the sender does not know who received the message
  • the first receiver to handle the message terminates the chain
  • the order of the receiver list matters
    • if the first and second receiver both know how to handle a certain type of message, some type of priority would have to be built into the list

Roles

  • the client
  • message handler interface
  • a series of concrete implementations of the interface

The client and handlers link together to form the chain of responsibility

Motivating example - Expense report approval

  • employee needs expense report approved
  • employee sends expense report to manager
    • expense is too high for manager approval
  • manager sends expense report to vice president
    • expense is too high for vice president approval
  • vice president sends expense report to president
  • president sends response

Demo of non-COR approach

  • iterate a list of employees with approval limits until we reach one who can approve
  • problem: even though this works, we are capturing the logic at the wrong level
    • if a manager can't approve, they don't have the employee go to the next higher manager, the manager does this on behalf of the employee

When to use

  • when you have more than one message handler for a message
  • when the appropriate handler is not explicitly known by the sender
  • when the set of handlers can be dynamically defined (this aspect may or may not be present)

Benefits

  • reduced coupling between sender and receiver
  • dynamic management of message handlers
  • end-of-chain behavior can be defined appropriately

Related patterns

  • Composite
    • where the Parent object could be used as next successor in the chain
  • Tree of Responsibility
    • like COR, but where each node could have multiple successors
      • could use multiple successors asynchronously or synchronously

Command

Non-Pluralsight notes

Github

Category: Behavioral

Non-Pluralsight sources:

UML

uml design pattern command

Roles

  • Client
    • creates and configures the Command object
    • passes all request parameters including a receiver to the command object
    • passes the command object to an Invoker
  • Invoker (a.k.a. Sender)
    • has a reference to the command interface
    • calls execute() (doesn't have to be "execute") on the command interface
  • Command Interface
  • Command Implementation
    • implements the command interface and holds a reference to the receiver
    • holds all state/context necessary to execute the command
    • calls the action on the receiver
  • Receiver
    • the object that executes the command
    • this may likely be a business logic object

Pluralsight notes

Other common names for this Pattern: "Action" and "Transaction"

Motivating Example

  • a command-line order management system
  • existing orders may be edited, and a log of all changes must be kept

Intent

  • represent an action as an object
  • decouple clients that execute the command from the details and dependencies of the command logic
  • enables delayed execution
    • can queue commands for later execution
    • if command objects are also persistent, can delay across process restarts

Applicability

  • logging
  • validation
  • undo

Structure

  • client
    • just calls the command--not responsible for passing in arguments
  • command
    • might have references to dependencies
    • might have validation functionality
    • might have logging functionality
    • might have undo functionality

Consequences

  • commands must be completely self contained
    • the client doesn't pass in any arguments
    • command must have everything it needs to get its job done
    • it's not uncommon to implement factories to build the commands
  • easy to add new commands
    • just add a new class (OCP)

Related patterns

  • factory pattern
    • factories are often used to construct command objects
  • null object (a "mini" pattern)
    • useful to return a "null command" that implements the interface rather than returning null
  • composite
    • a composite command could be useful
    • construct it with several "child" commands
    • Execute() on the composite command will call Execute() on the child commands

When to consider the command pattern

  • when you want to decouple the client that executes the command from the command logic and its dependencies
  • when you're building a command-line application
  • when you're implementing validation
  • when you're implementing undo

Composite

Non-Pluralsight notes

Github

Category: Structural

Non-Pluralsight sources:

UML

uml design pattern composite

Pluralsight notes

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

Example: email groups

  • we treat an individual email address the same as we treat an email group
  • we could list each individual email address or we could just type the group
    • either way, the email gets to everyone
  • the group is a composite of individual emails but it's treated as one recipient when we list it in the To: field

... demo (splitting gold among party members as reward for killing monster)

Roles

  • component
    • the interface that both leaf and composite will implement
  • leaf
    • does whatever actions are defined on the interface
  • composite
    • must have a mechanism for managing its children
      • children can be of type leaf or of type composite
      • if leaf, see bullet under leaf role
      • if composite, see bullets under composite role

With these roles we can create a tree structure.

When to use

  • Groups or collections
    • this is a big indicator that you might be able to use composite pattern
    • can you add an interface for both the groups/collections to implement as well as the individuals in the groups/collections?
  • trees
    • any time you work with a tree, at least consider whether the composite pattern is appropriate
  • distribution
    • any time you need to distribute things among other things, the composite pattern might be appropriate
    • like in the demo for distributing gold

Decorator

Non-Pluralsight notes

Github

Category: Structural

Non-Pluralsight sources:

UML

uml design pattern decorator

Pluralsight notes

Usage

  • Extend or alter the functionality of objects at runtime
  • Example use case: existing app lacks validation
    • use decorator pattern to add validation functionality without modifying existing objects

Intent

  • add functionality to existing objects dynamically
  • alternative to subclassing (which can lead to "class explosion")
  • flexible design
  • support OCP

Applicability

  • legacy systems
    • add new functionality without touching existing code
  • add functionality to UI controls
  • sealed classes

Roles

  • Component: the base class or interface
    • base for all concrete components as well as all decorators
  • Concrete Component
    • inherits from Component
    • the object to be decorated
  • Decorator
    • also inherits from Component
    • holds a reference to a Component
  • Concrete Decorator
    • adds responsibilities to the wrapped component

Event Aggregator

Non-Pluralsight notes

Github

  • example implementation: [TBD]
  • demo of example implementation: [TBD]
  • ... probably won't implement this in my design patterns project
    • this is complex enough that i think i'd rather wait for a project that could make use of this pattern

Pluralsight notes

Example

  • an order entry system with many screens
    • screens: Order, Order Details, Order History, Shipping
  • each/all screens need to be notified when:
    • an order is selected
    • an order is created
    • an order update is canceled
    • an order is saved
    • an order is deleted

Intent

  • simplify event registration by providing a single centralized store
    • makes it easier for subscribers to discover the events
  • reduce coupling between publishers and subscribers
    • subscribers don't know about event source
  • reduce friction for introducing new events
    • subscribers simply go to the event aggregator to get these events
  • reduce memory management issues related to eventing
    • with .NET events, subscribers must unsubscribe themselves - not doing so results in memory leaks

Event aggregator's job: to route from sources to subscribers

Applicability

  • you are building a composite application
    • composite app meaning multiple modules coordinating among each other, each performing different aspects of the app
  • you have complex screens or many screens
  • you have many publishers and subscribers
  • you have many events
  • new events are added frequently
  • static events are a red flag
    • event aggregator might be a good solution for this problem

Roles

  • Publishers: sources of events
    • publishers only know about the aggregator (they don't know about subscribers)
    • holds a reference to the aggregator
  • Event Aggregator
    • sits in between publishers and subscribers
    • observes all publishers and aggregates events
  • Subscribers: observe aggregator for events
    • subscribers observe the aggregator (they don't know about publishers)
    • holds a reference to the aggregator

How to use

  • publishers and subscribers each hold a reference to the aggregator
  • publishers call aggregator's publication methods to notify subscribers
  • subscribers call aggregator's subscription methods to receive notifications

Consequences

  • complete decoupling of publishers and subscribers
    • can reuse the components in other applications
    • easier to test the components
  • much more easy to add new events (Types) in the system (OCP)
  • reduced memory leaks

Known variations

Related Patterns

More info:

Facade

Non-Pluralsight notes

Github

Category: Structural

Non-Pluralsight sources:

UML

uml design pattern facade

Roles

  • Client
    • doesn't access subsystem classes directly--works through Facade interface
    • depends only on the simple Facade interface and is independent of the complex subsystem
  • Facade
    • an interface to the subsystem
  • Subsystem
    • the complex API/interface/class/classes providing the functionality the client needs

Pluralsight notes

Intent (a few possible reasons)

  • provide a purpose-driven interface to a large body of code or interface
  • provide a simplified interface to make a complex body of code or interface easier to consume
  • provide a unified interface to functionality that is spread among complex object interactions
  • wrap a poorly designed API in a better one

Consequences

  • use of existing API is simplified
  • the full capability of the underlying APIs will often not be available through the facade
  • the facade may need to be updated (or a new one created) to expose more underlying functionality

Factory

Non-Pluralsight notes

Github

Category: Creational

Non-Pluralsight sources:

UML

uml design pattern factory

Roles: Factory Method

  • Product Interface (or abstract class)
  • Concrete Products
  • Creator Interface (or abstract class)
  • Concrete Creators

Roles: Abstract Factory

  • Product Interfaces (or abstract classes)
  • Concrete Products
  • Creator Interfaces (or abstract classes)
  • Concrete Creators
  • Client (holds reference to abstract factory)

Pluralsight notes

Motivating Example

  • Unsure which concrete implementation of an interface i want
  • want to separate the creation of an object from its representation
  • lots of if/else/switch block deciding which type to instantiate

Intent

  • separate the creation of an object from the decision of which object to create
  • add new classes and functionality without breaking OCP
  • store which object to create outside of the program

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.

- GoF

  • define an interface (or abstract base class) for factory types to implement
  • defer object creation to multiple factories that implement the factory interface

Advantages

  • eliminate references to concrete classes
    • both factories and objects created by the factories implement interfaces--the interfaces are referenced
  • concrete factories can be inherited to provide even more specialization
  • rules for object initialization are centralized

Disadvantages

  • may need to create a factory just to get a concrete class delivered
  • inheritance hierarchy gets deeper with coupling between concrete factories and created classes

Abstract Factory

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

- GoF

  • now a concrete factory could implement any number of different types of objects within its "family" of types

Flyweight

Non-Pluralsight notes

Github

Category: Structural

Non-Pluralsight sources:

UML

uml design pattern flyweight

Roles (from GoF)

  • Flyweight interface
    • the interface allows flyweights to receive and act on extrinsic state
  • Concrete Flyweight with intrinsic state
    • implements the flyweight interface
    • contains intrinsic state, if any
    • must be sharable and independent of context
  • Unshared Flyweight (not necessarily always used)
    • implements the flyweight interface, but is not sharable (the interface enables sharing but does not enforce sharing)
    • it's common for unshared flyweight objects to have child flyweight (shared) objects (e.g. row and column classes containing character flyweights)
  • Flyweight factory
    • creates and manages flyweight objects
      • commonly uses something like a dictionary to enable looking up flyweights
      • could include reclaiming memory of flyweights no longer needed, but not necessarily, especially if the number of flyweights needed is fixed and small
    • ensures flyweights are shared properly by providing an existing instance or creating one if none exists
  • Client
    • client must obtain flyweights exclusively through the flyweight factory (must never instantiate one directly)
    • maintains a reference to flyweights
    • computes or stores extrinsic state of flyweights
    • responsible for passing extrinsic state to the flyweight

GoF Notes

  • Intent
    • use sharing to support large numbers of fine-grained objects efficiently
  • Motivation
    • document editor example - using objects to represent text characters
      • representing each character as an object allows for fine-grained flexibility
      • this approach poses memory and other run-time overhead concerns as documents grow in length
    • flyweight describes how to share objects to allow fine-grained flexibility without prohibitive cost
  • What is a Flyweight
    • a shared object that can be used in multiple contexts simultaneously
    • intrinsic state: sharable state; stored in the flyweight; independent of context
    • extrinsic state: not sharable; not stored in flyweight; depends on and varies with context
    • in the doc editor example:
      • the flyweight's intrinsic (stored/shared) state is the character
      • the extrinsic (passed-in) state is the coordinate position and styling
      • each occurrence of a given character refers to the flyweight instance within the shared pool of flyweight objects

Pluralsight notes

Intent

  • reduce memory costs for large number of objects
  • share objects to be used in multiple contexts simultaneously
  • retain object oriented granularity and flexibility

Consequences

  • replace large groups of objects by a few shared objects
    • achieved by
      • identifying and extracting out extrinsic state and requiring it to be passed in during runtime
      • using a flyweight factory to control the creation and sharing of instances
  • memory savings
    • reduced by using fewer instances
    • reduced by computing extrinsic state rather than storing it
⚠️ **GitHub.com Fallback** ⚠️