From 5841399d9a1b98e0fbac0d08c41c4cab5066bb8d Mon Sep 17 00:00:00 2001 From: Morten Daehli Aslesen Date: Sun, 26 Nov 2023 19:48:43 +0100 Subject: [PATCH] feat: complete blog post on behavioral design patterns --- content/page/about.md | 13 +- content/page/reading-list.md | 4 +- .../2023-04-11-behavioral-design-patterns.md | 366 +++++++++++++++++- pyproject.toml | 15 + 4 files changed, 388 insertions(+), 10 deletions(-) create mode 100644 pyproject.toml diff --git a/content/page/about.md b/content/page/about.md index 64df952..6a02c99 100644 --- a/content/page/about.md +++ b/content/page/about.md @@ -7,11 +7,18 @@ description = "Things about me." ![Morten Dæhli Aslesen](/images/2021/03/this-is-me.jpeg#align-center "This is me") -This is me 👋 Hi, I’m Morten Dæhli Aslesen, and I'm currently working as an IT-consultant at [Bouvet Norway ASA](https://www.bouvet.no) where I've been since 2020. My background is in Economics and Nanotechnology, but in my professional career I have been focusing on advanced analytics, machine learning, data platforms and software development. +This is me 👋 Hi, I’m Morten Dæhli Aslesen, an IT-consultant at [Bouvet Norway ASA](https://www.bouvet.no) +since 2020. My academical background is in Economics and Nanotechnology, but in my professional career I have been +focusing on advanced analytics, machine learning, data platforms and software development. -I'm an avid DevOps practitioner, I prefer simple elegant and pragmatic solutions, clean explicit code and trunk based development. In my spare time, I'm always working on something new. Solving something, learning something, creating something... Lately, I've been working on full-stack web applications, and I maintain two webshops for our family business [Min Symaskin](https://www.minsymaskin.no) and [Quiltefryd](https://www.quiltefryd.no). When I find the time, I will also write about my own personal projects and my thoughts about IT and coding here in this blog. +I'm an avid DevOps practitioner, my mantra is simplicity, elegance, and pragmatism. I strive for clean, explicit code +and champion trunk-based development.. In my spare time, I'm always working on something new. Solving something, +learning something, creating something... Lately, I've been working on full-stack web applications, and I maintain +two webshops for our family business Min Symaskin and Quiltefryd AS. When I find the time, I will also write about +my own personal projects and my thoughts about IT and coding here in this blog. -When I'm not hanging out with friends and family, and when I'm not in the digital world. Then I enjoy rock climbing, surfing, hiking, and generally everything that has to do with the great outdoors. +When I'm not hanging out with friends and family, and when I'm not in the digital world. Then I enjoy rock climbing, +surfing, hiking, and generally everything that has to do with the great outdoors. Check out my [Github profile](https://github.com/mortendaehli) if you are curious about what I’m up to! diff --git a/content/page/reading-list.md b/content/page/reading-list.md index 41086b2..417f456 100644 --- a/content/page/reading-list.md +++ b/content/page/reading-list.md @@ -7,7 +7,9 @@ tags = ["tech", "reading"] draft = false +++ -This is a curated list of reading material and code projects that I enjoy and recommend to others: +Welcome to my treasure trove of resources! In this curated list, I share the reading materials and code projects that +have not only enriched my understanding but also shaped my approach in the tech world. Each item here is more than just +a recommendation — it's a reflection of the philosophies and practices I believe in. ### Coding philosophy - [Twelve-factor app](https://12factor.net/) diff --git a/content/post/2023-04-11-behavioral-design-patterns.md b/content/post/2023-04-11-behavioral-design-patterns.md index 719656a..7e9cddb 100644 --- a/content/post/2023-04-11-behavioral-design-patterns.md +++ b/content/post/2023-04-11-behavioral-design-patterns.md @@ -296,68 +296,376 @@ Expression: 3 + 2 - 1 evaluates to 4 ``` ### Iterator Pattern +The Iterator pattern is a behavioral design pattern that provides a way to access the elements of an aggregate object +sequentially without exposing its underlying representation. This pattern decouples the collection from the traversal +logic and offers a standardized way to cycle through elements of the collection without needing to understand its +internal structure. It's especially useful in cases where a collection has a complex structure, but you want to hide +this complexity from the client. Note that this is already a built in feature in Python classes by if you define +__next__ and __iter__ methods. The Iterator Pattern consists of the following components: +- **Iterator**: An interface or abstract class that specifies methods for accessing and traversing elements. +- **Concrete Iterator**: Implements the Iterator interface and keeps track of the current position in the traversal of the aggregate object. +- **Aggregate**: An interface which declares the creation of the iterator object. +- **Concrete Aggregate**: Implements the Aggregate interface and returns an instance of the Concrete Iterator. ```python +from abc import ABC +from typing import Any, Sequence + + +class Iterator(ABC): + def next(self) -> Any: + pass + + def has_next(self) -> bool: + pass + + +class ConcreteIterator(Iterator): + def __init__(self, collection: Sequence) -> None: + self._collection = collection + self._index = 0 + + def next(self) -> Any: + if self.has_next(): + item = self._collection[self._index] + self._index += 1 + return item + raise StopIteration + + def has_next(self) -> bool: + return self._index < len(self._collection) + + +class Aggregate: + def create_iterator(self): + pass + + +class ConcreteAggregate(Aggregate): + def __init__(self, collection) -> None: + self._collection = collection + + def create_iterator(self) -> ConcreteIterator: + return ConcreteIterator(self._collection) + + +if __name__ == "__main__": + collection = ConcreteAggregate(['Item 1', 'Item 2', 'Item 3']) + iterator = collection.create_iterator() + + while iterator.has_next(): + print(iterator.next()) ``` This will output: ```text - +Item 1 +Item 2 +Item 3 ``` ### Mediator Pattern +The Mediator pattern is a behavioral design pattern that enables a set of objects to communicate with each other +through a mediator object. This pattern reduces direct communications between objects and makes their interaction +easier to manage and extend. It's particularly useful in scenarios where multiple objects need to work together, +but direct references between them would lead to a tightly-coupled system that's hard to maintain and understand. +The mediator acts as a central hub for interaction, facilitating the exchange of messages or data between objects. The Mediator Pattern consists of the following components: +- **Mediator**: An interface that defines the methods used for communication between various components. +- **Concrete Mediator**: Implements the Mediator interface and coordinates communication between various components. + It knows all the components and acts as a router of messages. +- **Component**: A class that communicates with other components through the mediator instead of direct references. ```python +class Mediator: + def notify(self, sender, event): + pass + + +class ConcreteMediator(Mediator): + def __init__(self, component1, component2): + self._component1 = component1 + self._component1.mediator = self + self._component2 = component2 + self._component2.mediator = self + + def notify(self, sender, event): + if event == "A": + print("Mediator reacts on A and triggers following operations:") + self._component2.do_c() + elif event == "D": + print("Mediator reacts on D and triggers following operations:") + self._component1.do_b() + + +class BaseComponent: + def __init__(self, mediator=None): + self._mediator = mediator + + @property + def mediator(self): + return self._mediator + + @mediator.setter + def mediator(self, mediator): + self._mediator = mediator + +class Component1(BaseComponent): + def do_a(self): + print("Component 1 does A.") + self.mediator.notify(self, "A") + + def do_b(self): + print("Component 1 does B.") + + +class Component2(BaseComponent): + def do_c(self): + print("Component 2 does C.") + + def do_d(self): + print("Component 2 does D.") + self.mediator.notify(self, "D") + + +if __name__ == "__main__": + c1 = Component1() + c2 = Component2() + mediator = ConcreteMediator(c1, c2) + + print("Client triggers operation A.") + c1.do_a() + + print("\nClient triggers operation D.") + c2.do_d() ``` This will output: ```text - +Client triggers operation A. +Component 1 does A. +Mediator reacts on A and triggers following operations: +Component 2 does C. + +Client triggers operation D. +Component 2 does D. +Mediator reacts on D and triggers following operations: +Component 1 does B. ``` ### Memento Pattern +The Memento pattern is a behavioral design pattern that allows an object to save its state and restore it later, +without revealing the details of its implementation. This pattern is particularly useful for implementing features like +undo mechanisms or for saving and restoring the state of an object at a particular point in time. It helps in +maintaining high encapsulation levels as the internal state of an object is not exposed outside, yet can be saved externally. The Memento Pattern consists of the following components: +- **Memento**: A class that stores the internal state of the Originator object. It should have two interfaces; one for + the caretaker (which is usually wide) and one for the originator (which is narrow and allows the originator to access + any necessary properties). +- **Originator**: The class of which the state is to be saved. It creates a memento containing a snapshot of its current + internal state and can also use the memento to restore its internal state. +- **Caretaker**: Responsible for the safekeeping of the memento. It can request a memento from the originator and store + it for future use. The caretaker, however, must not modify or operate on the contents of the memento. ```python +class Memento: + def __init__(self, state): + self._state = state + def get_saved_state(self): + return self._state + + +class Originator: + _state = "" + + def set(self, state): + print(f"Originator: Setting state to {state}") + self._state = state + + def save_to_memento(self): + print("Originator: Saving to Memento.") + return Memento(self._state) + + def restore_from_memento(self, memento): + self._state = memento.get_saved_state() + print(f"Originator: State after restoring from Memento: {self._state}") + + +class Caretaker: + _saved_states = [] + + def add_memento(self, memento): + self._saved_states.append(memento) + + def get_memento(self, index): + return self._saved_states[index] + + +if __name__ == "__main__": + originator = Originator() + caretaker = Caretaker() + + originator.set("State1") + caretaker.add_memento(originator.save_to_memento()) + + originator.set("State2") + caretaker.add_memento(originator.save_to_memento()) + + originator.set("State3") + caretaker.add_memento(originator.save_to_memento()) + + originator.restore_from_memento(caretaker.get_memento(1)) ``` This will output: ```text - +Originator: Setting state to State1 +Originator: Saving to Memento. +Originator: Setting state to State2 +Originator: Saving to Memento. +Originator: Setting state to State3 +Originator: Saving to Memento. +Originator: State after restoring from Memento: State2 ``` ### Observer Pattern +The Observer pattern is a fundamental behavioral design pattern that establishes a one-to-many relationship between +objects. It allows an object, known as the subject, to notify a set of observers about any changes in its state. +This pattern is essential in scenarios where a change in one object requires changing others, and it particularly +shines in cases where the number of dependent objects is unknown or dynamic. It ensures a high degree of separation and +decoupling between the subject and observers, promoting modular code. The Observer Pattern consists of the following components: +- **Subject**: Maintains a list of observers and provides methods to attach or detach observers to it. It notifies the + observers about any state changes. +- **Observer**: An interface or abstract class with a method to update the observer, which gets called by the subject + it is observing. +- **Concrete Observer**: Implements the Observer interface and maintains a reference to a concrete subject. It + implements the update method to react to state changes in the subject. ```python +class Subject: + def __init__(self): + self._observers = [] + + def attach(self, observer): + if observer not in self._observers: + self._observers.append(observer) + + def detach(self, observer): + try: + self._observers.remove(observer) + except ValueError: + pass + + def notify(self): + for observer in self._observers: + observer.update(self) + + +class Observer: + def update(self, subject): + pass + + +class ConcreteObserverA(Observer): + def update(self, subject): + if subject.state < 3: + print("ConcreteObserverA: Reacted to the event") + + +class ConcreteObserverB(Observer): + def update(self, subject): + if subject.state >= 3: + print("ConcreteObserverB: Reacted to the event") + + +class ConcreteSubject(Subject): + _state = 0 + + @property + def state(self): + return self._state + + @state.setter + def state(self, val): + self._state = val + self.notify() ``` This will output: ```text - +ConcreteObserverA: Reacted to the event +ConcreteObserverB: Reacted to the event ``` ### State Pattern +The State pattern is a behavioral design pattern that allows an object to alter its behavior when its internal state +changes. This pattern is used to encapsulate varying behavior for the same routine, based on the object's state. It's +like having a class change its class. This pattern is particularly useful in scenarios where an object needs to change +its behavior at runtime without changing its class. It helps in organizing the code related to particular state-related +behaviors and allows for adding new states without changing existing state classes or the context. The State Pattern consists of the following components: +- **Context**: Holds a reference to a state object that defines the current state. The context delegates state-specific + behavior to the current state object. +- **State**: An interface or abstract class defining the state-specific behavior. +- **Concrete States**: Implement the State interface and provide the implementation for the state-specific behavior. ```python +class State: + def handle(self, context): + pass + + +class ConcreteStateA(State): + def handle(self, context): + print("State A handling and switching to State B") + context.state = ConcreteStateB() + + +class ConcreteStateB(State): + def handle(self, context): + print("State B handling and switching to State A") + context.state = ConcreteStateA() + + +class Context: + def __init__(self, state: State): + self._state = state + + @property + def state(self): + return self._state + + @state.setter + def state(self, state: State): + self._state = state + + def request(self): + self._state.handle(self) + + +if __name__ == "__main__": + context = Context(ConcreteStateA()) + + context.request() # Handling in State A + context.request() # Handling in State B ``` This will output: ```text - +State A handling and switching to State B +State B handling and switching to State A ``` ### Strategy Pattern @@ -493,16 +801,62 @@ ConcreteClassB: Step 3 ``` ### Visitor Pattern +The Visitor pattern is a behavioral design pattern that allows adding new operations to existing object structures +without modifying them. This pattern is particularly useful when dealing with a complex object structure, such as a +composite object. It allows one to separate an algorithm from the object structure on which it operates, providing a +way to add new operations without altering the structures of the objects. It's especially helpful in cases where most +of the functionality in a system is dependent on the concrete classes of objects. The Visitor Pattern consists of the following components: +- **Visitor**: An interface or abstract class defining the visit() methods for each type of concrete element. +- **Concrete Visitor**: Implements the Visitor interface and defines the operation to be performed on each type of concrete element. +- **Element**: An interface or abstract class with a method accept() that takes a visitor as an argument. +- **Concrete Element**: Implements the Element interface and defines the accept() method such that it calls the visit() method on the visitor object. ```python +class Visitor: + def visit_element_a(self, element): + pass + + def visit_element_b(self, element): + pass + + +class ConcreteVisitor(Visitor): + def visit_element_a(self, element): + print("ConcreteVisitor: Visiting Element A") + def visit_element_b(self, element): + print("ConcreteVisitor: Visiting Element B") + + +class Element: + def accept(self, visitor): + pass + + +class ConcreteElementA(Element): + def accept(self, visitor): + visitor.visit_element_a(self) + + +class ConcreteElementB(Element): + def accept(self, visitor): + visitor.visit_element_b(self) + + +if __name__ == "__main__": + elements = [ConcreteElementA(), ConcreteElementB()] + visitor = ConcreteVisitor() + + for element in elements: + element.accept(visitor) ``` This will output: ```text - +ConcreteVisitor: Visiting Element A +ConcreteVisitor: Visiting Element B ``` ## See also diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..ee87143 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,15 @@ +[tool.poetry] +name = "mortendaehli-github-io" +version = "0.1.0" +description = "" +authors = ["Morten Daehli Aslesen "] +readme = "README.md" +packages = [{include = "mortendaehli"}] + +[tool.poetry.dependencies] +python = "^3.11" + + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api"