--- title: Design Patterns by Construx created_date: 2025-01-16 updated_date: 2025-01-16 aliases: tags: - course type: course course_name: Design Patterns author: Construx source: https://construx.vueocity.com/portal/design-patterns status: not_started --- # Design Patterns by Construx - **🏷️Tags** : #01-2025 #book --- ## Summary > [!summary] Summary > 3 Sentences only! > - What are the main ideas? > - If I implemented one idea from this book right now, which one would it be? > - How would I describe the course to someone else? --- ## Ideas and Thoughts > [!info]+ Inspiring Questions > - Did you think about other concepts from other resources? > - Do the concepts fit to your past, to your memories? > - Can you relive them and reflect them from a different angle? --- ## Introduction ### Getting Started > [!definition] UML Notation > Contents > [!Definition] What is a Design Pattern > A standard solution to a recurring problem. > > We should not reinvent the wheel. Patterns are much more reusable than code. A *bridge* is a design pattern because it is a standard solution to a recurring problem: we need to get things across a not passable obstacle. For every problem you have *design criteria* to choose the best type of bridge (e.g. span, clearance, loads, cost, etc.). In design patterns design criteria are called *Goal forces* and *constraint forces*. As a beginner: focus on the problem, not the solutions! ### UML Notations: #### Note ![[Pasted image 20250116161217.png]] #### Class, Attribute and Operation ![[Pasted image 20250116160836.png]] Operations are viewed from extern Methods are viewed from intern ### Minimize Overall Complexity Measures of complexity - Cyclomatic complexity: the number of decisions that are being made --> local complexity - Depth of decision making: embedding of decision within decision and decision --> local complexity - number of parameters: --> global complexity - fan out: number of functions that are called by the function --> global complexity ![[Pasted image 20250116161534.png]] Local complexity is a measure on how complicated each function is, whereas the global complexity is the complexity in between functions. Remember to get an appropriate balance. ### Use Abstractions **Syntax**: Is all about structure **Semantics**: Is all about meaning - Colorless green dreams sleep furiously: correct syntax, completely wrong semantics --> defective or buggy code, because it is semantically meaningless The *compiler* is a master of syntax but cannot know anything about semantics. The programmer needs to focus much more on semantics, because the complier will take care of the syntax. > [!NOTE] Abstraction > the principle of ignoring those aspects of a subject that are not relevant to the current purpose in order to concentrate solely on those that are. > > It is a tool to reduce and manage complexity. what is the role of one function / subroutine? The more abstraction you have, the less performant the code becomes. Abstraction is not learnable. Good abstractions lead to clean code, which is maintainable and leads to less defects. ### Encapsulate Design Decisions Abstraction: permission to ignore Encapsulation: I prevent you from knowing Meaning they are not the same thing. Goal: - **hide design decision** (data representation, algorithms, etc.) - **Modules should be black boxes**: programm to an interface, not through an interface Idea: Design by Contract Requires vs Guarantees A function promises to give certain outputs depending on the inputs This is important for documentation of code if the code is used by different people. Docstrings for example. We only worry about semantic things. Explain exactly what the input values should be and what the return values will be. Explain exactly what errors and exceptions. Include Constraints on state (function may open file), include performance limits (use no more than 200 bytes per instance, etc). Defensive contract: requires less and guarantees more (check input for example). Contracts are great, because you can treat the function as a blackbox. When you modify the contract you need to inform all the users of the function about the updates of the contract **Desing by Contract is the means to encapsulation** Use: **Purpose, Requires and Guarantees** ### Maximize Cohesion, Minimize Coupling **Cohesion**: Indivisibility of a given part: Does this function do exactly one thing? It should also not split functionality into two separate functions **Coupling**: Dependence between parts: Do not connect things that don't need to be connected. If you must connect things, connect them such that they can be disconnected as easily as possible. Goal: Highly cohesive, very loosely coupled Generally software solves problems by *Decomposition, solution, recomposition*. There are an infinite amount of different solutions, which have different quality. Cohesion and Coupling characterise the quality of the solution. ![[Pasted image 20250116171230.png]] Always try to Decouple code: | **Avoid** | **Do** | | ----------------------------------- | -------------------------------------------------------------------------------------------------------- | | Global variables (form of coupling) | Design by Contract | | Friend (C++) | Dependency inversion: if you depend on things, depend on the most generic thing (type of car, not model) | | Self-modifying code | Publish-subscribe (observer pattern) | | Reflection | Law of Demeter | #### Law of Demeter Principle of least knowledge. a.b().c() breaks the law --> violates coupling a.b() doesn't break the law ### Design for Invariants, Design for Change Product Families: attack different target markets with different value propositions with the same base platform: (pickup truck and suv example. For ford it's the same car (slight difference in build but 95% the same)). The **core of the software design is made for the invariants**, meaning all the things that do not change and are the same throughout the software. An example might be the base driver code and the base I2C/SPI interface, which all the drivers use. Every driver has different registers that need to be set with different values, but the core structure can be designed to be invariant. Design for Change means the same thing: to *separate common from variable functionality*: use templates, inheritance, conditional compilation, frameworks. *Hide variation behind abstract interfaces*: Outside sees a routine that moves data from A to B and doesn't care how it is being done. It can be done by ftp, scp, tcp/ip, bluetooth, etc. Design patterns that make abstract interfaces are adapter, bridge, strategy, factory method, abstract factory, template method, iterator, decorator, proxy, etc. The client side code does not need to be changed when the bridge implementation changes. *Use delayed-binding strategies*: C does early binding, python does very late binding. Examples: named constants, configuration/preference files, dependency injection/ function pointers, inversion of control, data-driven design (set of data to configure software, e.g xml files or so?), self-configuration (android: different hardware configurations. It pings the hardware upon boot and asks about specifications of the hardware.). A problem is that usually software is developed sequentially: first for customer A, then customer B, etc. Better would be to find out about more about the landscape before starting the development. ### UML: Association and Multiplicity **Association**: represents links between objects **Multiplicity**: constraints on number of links ![[Pasted image 20250117100749.png]] Multiplicity: 1 to many. If you are a customer must you have at least 1 order? The \*-Notation means that it can also be 0. There are four cases which are noted differently: | Lower Bound | Upper Bound | UML Notation | | ----------- | ----------- | ------------ | | 0 | 1 | 0..1 | | 0 | Many | \* | | 1 | 1 | 1 | | 1 | Many | 1..\* | Note that it can also be specific numbers: e.g. 3..7 #### UML Notation: Composition and Aggregation Composition: exclusive membership: ♦ Aggregation: non-exclusive membership: ♢ When using composition and aggregation, be specific about the multiplicity. Diamonds are trendy and are often misused: you can specify multiplicity to represent the exact same thing as composition and aggregation. ### UML: Sequence Diagram Describes interactions between objects (dynamics). Primary Concepts: Objects, lifeline, activation context, message Naming: objectName:class, objectName, :class ![[Pasted image 20250117102354.png]] ---- is the lifeline time goes from top to bottom opt: optional \[too cold] is a condition ### UML: Inheritance and Abstraction An arrow pointing to the base class is done by Inheritance ![[Pasted image 20250117102805.png]] An abstract class is a base class, which itself cannot create any objects, have any instances. It just creates an interface. If shape was abstract you could only create rectangles and circles, but no other shapes. Abstract methods within abstract classes need to be implemented ### Design Principle: Liskov Substitutability T (superclass) <-- S (subclass) Goal: objects of class T may be replaced with objects of class S without altering any desirable properties of that program (e.g. correctness) - preconditions in subclass S as strict or less strict than in superclass T - postconditions in subclass S as strict or less strict than in superclass T - No new exceptions should be introduced in subclass S --> A subclass S must require no more and promise no less than its superclass T Example: the contract requires must be the same or less strict (preconditions) and for the guarantees the subclass must guarantee the same or more than the superclass T (postconditions) Be cautious about *accidental polymorphisms*: a completely different class that uses the same names. Usually the requires and guarantees are very different. This means syntax is the same, but semantics are different. Syntactically substitutionable but not semantically substitubable. #### Design Principle: Favor Association over Inheritance Inheritance cannot change the behaviour at run-time: a rectangle shape cannot morph into a circle shape. ## Design Patterns Patterns are not mutually exclusive and can be used on top of each other. ### Adapter Pattern - No prerequisits - Usually problems have same semantic, but a different syntax. - Reasons: too much client code to change, code not accessible, many clients that use the same code - Adapters are an intermediate code block that adapts the syntax between the two parts. - meaning you have one more layer, thus slightly less performance. - ![[Pasted image 20250117112716.png]] - use an adapter base class (PCLinuxGraphicsAdapter) - There is no guarantee that it is only the syntax that changes, maybe you need to adapt the semantics as well. - Object vs class adaption. - In multiple inheritance classes like C++ you inherit from both Target and Adaptee - ![[Pasted image 20250117115604.png]] - Relationship to fundamental design principles - design to invariants/desing for change - encapsulation - liskov substitutability - high cohesion/loose coupling > [!Important] Key Points > - minimizes code changes > - portability across multiple providers > - two versions: object vs class adapter Interesting application: In testing you might want to test hidden / private functions. So you can create a inherited object lets call it test-object, where those functions are exposed. So the test-object is basically an adapter. ### Façade (also called Wrapper) Build a wrapper around a complex service to simplify the interface. - Depending on the language you might not be able to preventing the client to go around the facade. - An adapter can be a wrapper. It all depends on your intent: If your intent is syntax adapting it is an adapter. If your intent is hiding complexity it is a wrapper. It can be both at the same time if your intent is to do both at the same time #### Design principles - Encapsulation - Abstraction (hide details that are not needed) - High cohesion / loose coupling (secondary, decouple the service provider) ### Bridge **Prerequisite**: Adapter Pattern The bridge pattern is very dynamic and can be changed during runtime very quickly (strategy pattern is more static.) Remember the fundamental design pattern of favouring association over inheritance. In a CNC-milling machine example we have 60 different subclasses of a programStep, which all cut different cuts. If I want to add a new communication protocol to communicate with the machine (tcp instead of serial) I would have to add another layer which adds another 60 subclasses, and so forth. This would be really bad and this is where the bridge pattern thrives: Goal force: decouple abstraction from implementation so that the two can vary independently. There are variations on two dimensions: the type of cut, the type of communication. Constraint force: layer of indirection: slight performance hit **Abstraction and Implementation can vary independently!** ![[Pasted image 20250117141550.png]] Bridge is often inherently an Adapter. > [!NOTE] No definition of how "abstractions" get bound to "implementors" > - statically at "abstraction" instantiation > - dynamically as parameter or dependency injection? #### Design principles - favor association over inheritance - high coehision/ loose coupling - design to invariants / design for change - liskov substitutability ![[Pasted image 20250117142732.png]] ### Strategy Forces: - behavior needs to change at runtime - support different solutions to a particular problem - insulate clients from details of the solutions - added layer of indirection is acceptable (hit on performance) ![[Pasted image 20250117152338.png]] > [!NOTE] Implementation Note > No definition of how nor when "context" gets bound to "concrete strategies" > - client decides > - broker or agent decides > - parameter > - dependency injection > > Concrete strategies need identical interface but maybe not the same parameters Example: sorting algorithms different sorting algorithms have different properties. Strategy design pattern can be used to let clients use different sort algorithms #### Design principles - design to invariants / design for change - encapsulation - liskov substitutability - high cohesion / loose coupling > [!Important] Key Points > Strategy defines family of algorithms, encapsulates each one and makes them interchangable ### Composite No prerequisite Allows to use recursion: example: make folders inside folders inside folders #### Forces - need to represent hierarchical structures - need to treat primitive elements and composites as uniformly as possible - copying, moving, deleting files has same effect as on folder - resizing one shape on drawing has same effect as resizing group of shapes What you can do with an individual you should do with a group ![[Pasted image 20250117153232.png]] ![[Pasted image 20250117153316.png]] > [!NOTE] Implementation Notes > - depth first vs breadth-first might be relevant in operations that traverse structure. > - ordering of compoonents in composite might be relevant > - shallow copy vs deep copy (**implement in contract! Should be crystal clear**) > - **shallow**: Only immediate object copied, not sub-referenced objects > - **deep**: copy object and all sub-referenced objects. #### Design Principles - Abstraction - Design to invariants/design for change - encapsulation - liskov substitutability - high cohesion / loose coupling > [!Important] Key Points > - hierarchical structure > - treat primitive elementss and composites as uniformly as possible > - copying, moving, deleting > - resizing single shape has same effect as resizing multiple grouped shapes > - be careful with copy() semantics --> communicate through contract ### Observer aka. Publish-Subscribe prerequisites: None aka: Implicit Invocation, Publish-Subscribe Forces: - 1-to-many dependency requiring object to notify many other objects when it changes state - need to limit direct knowledge the many and the one have of each otehr - added layer of indirection is acceptable (performance hit) ![[Pasted image 20250117160123.png]] > [!Note] Implementation Notes > - same notification interface for all observers on same subject (even if information needs are different) #### Design Principles ### Template Method ### Factory Method ### Abstract Factory ### Singleton ### Iterator ### Proxy ### Decorator ### Command ### State ### Data Access Object --- ## Exercises --- ```query Design Patterns Construx -file: "Design Patterns by Construx.md" ```