Skip to content

design patterns

Marcel Schmalzl edited this page Jun 12, 2024 · 1 revision

Design Patterns

Generic solution (pattern) catalogue to solve recurring common situations in a certain context. It's important to mention that we try to separate the pattern from the actual implementation.

  • GoF (gang of four) book
    • "UML" diagrams: not as the current standard (at the time of writing the GoF book the UML standard did not exist yet)
    • Usage: mostly obsolete
  • Using design patterns could cause longer run-time; however typically only some specific part of a program is critical (for real-time processing); for all other parts design patterns can and should be used
    • Questions useful for an evaluation:
      • When do we get delays? (init, during time-critical code execution, background process, ...)
      • How often do we need to pay the overhead? (1000 times per second, once (during init), edge-cases, ...)
      • Which amount of (time) cost is caused by the pattern

General

  • Usage
    • Detect useful patterns (where and which pattern for a certain problem)
    • Adaption of patterns to actual use case (not always 1:1 applicable)
  • Pattern classification and names
    • Creational: Abstract an object creation process (make a system independent of the way its objects are created, composited or represented)
      • Abstract factory: Families of objects without specifying the exact underlying classes
      • Builder
      • Factory Method: Creates an obj. without specifying the exact underlying class
      • Prototype
      • Singleton
    • Structural: Define how classes/objects are composited (mostly to create a greater structure)
      • Adapter
      • Bridge
      • Decorator
      • Facade
      • Flyweight
      • Composite
      • Proxy
    • Behavioural: Communication and responsibilities between objects
      • Command
      • Observer
      • Visitor
      • Interpreter
      • Iterator
      • Memento
      • Template Method
      • Strategy
      • Mediator
      • State
      • Chain of Responsibility

Creational patterns

Abstract factory

Builder

Factory

  • Relation between patterns (from easy to (complex + flexible)): Factory Method -> Abstract Factory -> Prototype -> Builder
  • Factory Method vs. Builder:
    • Factory method is usually just a wrapper function where the entire object has will be created at once.
    • For the builder (which is a wrapper object) it happens usually step-wise (esp. when the object cannot produced in one step (e.g. de-serialization process for a complex object)) - e.g. using setter methods to set/modify the creation behaviour

Factory Method

Prototype

Singleton

Structural patterns

TODO: Show an example in C++ in as few lines of code as possible. With that example (potentially use comments) show the difference between tight coupling and loose coupling; use the newest C++ standard and best practices; use cars and/or car manufacturers as an example for meaningful class names; use maximum 10 lines of code for all if possible.

Behavioural patterns

Design Principles

Y A G N I
o i o e t
u n n e
  ' n d
  t a

=> Implement only what you need (don't try to predict the future in your implementation)

O C P
p l r
e o i
n s n
  e c
    i
    p
    l
    e
  • A class should be open for extension but closed to modification
  • Derived classes should extend the base class(es) without changing their behaviour (-> Liskov Substitution Principle)
D R Y
o e o
n p u
' e r
t a s
  t e
    l
    f

=> Generalise

S R P
i e r
n s i
g p n
l o c
e n i
  s p
  i l
  b e
  i
  l
  i
  t
  y

=> 1 class/function/... should only have one purpose

Dependency Inversion Principle:

High-level modules should not import anything from low-level modules. Both should depend on abstractions (e.g., interfaces). Abstractions should not depend on details. Details (concrete implementations) should depend on abstractions. (from: https://en.wikipedia.org/wiki/Dependency_inversion_principle)

Tight vs loose coupling:

// Tight Coupling
class BMW { public: void startBMW() {} };
class Driver1 { BMW car; public: void go() { car.startBMW(); } };           // Driver1 is tightly coupled with BMW (BMW might use other functions then a Mercedes => hard to maintain)


// Loose Coupling
class ICar { public: virtual void start() = 0; };                           // Interface for Car (abstracts the all cars)
                                                                            // pure virtual to force implementing the interface
class Ferrari : public ICar { void startFerrari() { /* ... */ } public: void start() override { startFerrari(); } };           // Implement start of ICar
class Driver2 {
    ICar& car;                          // Use ICar instead of the actual car to make it independent of the manufacturer (-> loose coupling)
public:
    Driver2(ICar& c) : car(c) {}
    void go() { car.start(); }
};

class Mercedes : public ICar { void startMercedes() { /* ... */ } public: void start() override { startMercedes(); } };

Mercedes mercedes;
Driver2 driver2(mercedes);              // Using Mercedes with Driver2
driver2.go();                           // Start Mercedes
Clone this wiki locally