{"id":17168725,"url":"https://github.com/sanix-darker/solid_learn","last_synced_at":"2026-01-06T01:04:34.338Z","repository":{"id":243223862,"uuid":"811825133","full_name":"Sanix-Darker/solid_learn","owner":"Sanix-Darker","description":"SOLID learn","archived":false,"fork":false,"pushed_at":"2024-06-07T11:39:19.000Z","size":37,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-01-29T23:27:57.980Z","etag":null,"topics":["learning","solid","tutorial"],"latest_commit_sha":null,"homepage":"https://sanix-darker.github.io/solid_learn/solid.html","language":"HTML","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Sanix-Darker.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-06-07T11:29:56.000Z","updated_at":"2024-06-07T12:18:20.000Z","dependencies_parsed_at":"2024-06-07T12:59:36.299Z","dependency_job_id":"6450af55-b76e-49c4-85db-75c1a8033151","html_url":"https://github.com/Sanix-Darker/solid_learn","commit_stats":null,"previous_names":["sanix-darker/solid_learn"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Sanix-Darker%2Fsolid_learn","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Sanix-Darker%2Fsolid_learn/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Sanix-Darker%2Fsolid_learn/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Sanix-Darker%2Fsolid_learn/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Sanix-Darker","download_url":"https://codeload.github.com/Sanix-Darker/solid_learn/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245330856,"owners_count":20597846,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["learning","solid","tutorial"],"created_at":"2024-10-14T23:12:41.402Z","updated_at":"2026-01-06T01:04:29.319Z","avatar_url":"https://github.com/Sanix-Darker.png","language":"HTML","readme":"# SOLID PRINCIPLES AND DESIGN PATTERNS\n\n## LEARN MAP\n\n![map](./solid.svg)\n\n[CHECK THE INTERACTIVE MAP](https://sanix-darker.github.io/solid_learn/solid.html)\n\n## CONCEPTS\n\n- What are SOLID Principles?\n  - Five principles of object-oriented programming\n  - Introduced by Robert C. Martin\n- Importance of SOLID Principles\n  - Maintainability\n  - Extensibility\n  - Reusability\n\n## SOLID Principles\n- Single Responsibility Principle (SRP)\n  - Definition: A class should have only one reason to change.\n  - Example:\n    ```python\n    class Book:\n        def __init__(self, title, author):\n            self.title = title\n            self.author = author\n\n        def get_title(self):\n            return self.title\n\n        def get_author(self):\n            return self.author\n\n    class Printer:\n        def print_book(self, book):\n            print(f\"Title: {book.get_title()}, Author: {book.get_author()}\")\n\n    book = Book(\"Clean Code\", \"Robert C. Martin\")\n    printer = Printer()\n    printer.print_book(book)\n    ```\n- Open/Closed Principle (OCP)\n  - Definition: Classes should be open for extension but closed for modification.\n  - Example:\n    ```python\n    from abc import ABC, abstractmethod\n\n    class Shape(ABC):\n        @abstractmethod\n        def draw(self):\n            pass\n\n    class Circle(Shape):\n        def draw(self):\n            print(\"Drawing Circle\")\n\n    class Square(Shape):\n        def draw(self):\n            print(\"Drawing Square\")\n\n    class ShapeDrawer:\n        def __init__(self, shape):\n            self.shape = shape\n\n        def draw_shape(self):\n            self.shape.draw()\n\n    circle = Circle()\n    square = Square()\n\n    circle_drawer = ShapeDrawer(circle)\n    square_drawer = ShapeDrawer(square)\n\n    circle_drawer.draw_shape()\n    square_drawer.draw_shape()\n    ```\n- Liskov Substitution Principle (LSP)\n  - Definition: Objects of a superclass should be replaceable with objects of its subclasses without affecting the functionality.\n  - Example:\n    ```python\n    class Bird:\n        def fly(self):\n            pass\n\n    class Sparrow(Bird):\n        def fly(self):\n            print(\"Sparrow flying\")\n\n    class Ostrich(Bird):\n        def fly(self):\n            raise NotImplementedError(\"Ostrich cannot fly\")\n\n    def make_bird_fly(bird):\n        bird.fly()\n\n    sparrow = Sparrow()\n    ostrich = Ostrich()\n\n    make_bird_fly(sparrow)\n    make_bird_fly(ostrich)\n    ```\n- Interface Segregation Principle (ISP)\n  - Definition: Clients should not be forced to depend on interfaces they do not use.\n  - Example:\n    ```python\n    from abc import ABC, abstractmethod\n\n    class Printer(ABC):\n        @abstractmethod\n        def print_document(self, document):\n            pass\n\n    class Scanner(ABC):\n        @abstractmethod\n        def scan_document(self, document):\n            pass\n\n    class Fax(ABC):\n        @abstractmethod\n        def fax_document(self, document):\n            pass\n\n    class AllInOne(Printer, Scanner, Fax):\n        def print_document(self, document):\n            print(f\"Printing document: {document}\")\n\n        def scan_document(self, document):\n            print(f\"Scanning document: {document}\")\n\n        def fax_document(self, document):\n            print(f\"Faxing document: {document}\")\n\n    all_in_one = AllInOne()\n    all_in_one.print_document(\"Report\")\n    all_in_one.scan_document(\"Invoice\")\n    all_in_one.fax_document(\"Contract\")\n    ```\n- Dependency Inversion Principle (DIP)\n  - Definition: High-level modules should not depend on low-level modules. Both should depend on abstractions. Abstractions should not depend on details. Details should depend on abstractions.\n  - Example:\n    ```python\n    from abc import ABC, abstractmethod\n\n    class NotificationService(ABC):\n        @abstractmethod\n        def send_notification(self, message):\n            pass\n\n    class EmailNotification(NotificationService):\n        def send_notification(self, message):\n            print(f\"Sending email notification: {message}\")\n\n    class SMSNotification(NotificationService):\n        def send_notification(self, message):\n            print(f\"Sending SMS notification: {message}\")\n\n    class NotificationSender:\n        def __init__(self, notification_service):\n            self.notification_service = notification_service\n\n        def send_notification(self, message):\n            self.notification_service.send_notification(message)\n\n    email_notification = EmailNotification()\n    sms_notification = SMSNotification()\n\n    email_sender = NotificationSender(email_notification)\n    sms_sender = NotificationSender(sms_notification)\n\n    email_sender.send_notification(\"Meeting reminder\")\n    sms_sender.send_notification(\"Payment due\")\n    ```\n\n## Introduction to Design Patterns\n- What are Design Patterns?\n  - Reusable solutions to common problems\n  - Cataloged by Gang of Four (GoF)\n- Categories of Design Patterns\n  - Creational Patterns\n  - Structural Patterns\n  - Behavioral Patterns\n- Benefits of Design Patterns\n  - Encapsulation\n  - Flexibility\n  - Scalability\n\n## Creational Patterns\n- Singleton Pattern\n  - Definition: Ensures a class has only one instance and provides a global point of access to it.\n  - Example:\n    ```python\n    class Singleton:\n        _instance = None\n\n        def __new__(cls):\n            if cls._instance is None:\n                cls._instance = super().__new__(cls)\n            return cls._instance\n\n    singleton1 = Singleton()\n    singleton2 = Singleton()\n\n    print(singleton1 == singleton2)  # Output: True\n    ```\n- Factory Method Pattern\n  - Definition: Defines an interface for creating objects, but allows subclasses to alter the type of objects that will be created.\n  - Example:\n    ```python\n    from abc import ABC, abstractmethod\n\n    class Animal(ABC):\n        @abstractmethod\n        def speak(self):\n            pass\n\n    class Dog(Animal):\n        def speak(self):\n            return \"Woof\"\n\n    class Cat(Animal):\n        def speak(self):\n            return \"Meow\"\n\n    class AnimalFactory(ABC):\n        @abstractmethod\n        def create_animal(self):\n            pass\n\n    class DogFactory(AnimalFactory):\n        def create_animal(self):\n            return Dog()\n\n    class CatFactory(AnimalFactory):\n        def create_animal(self):\n            return Cat()\n\n    dog_factory = DogFactory()\n    cat_factory = CatFactory()\n\n    dog = dog_factory.create_animal()\n    cat = cat_factory.create_animal()\n\n    print(dog.speak())  # Output: Woof\n    print(cat.speak())  # Output: Meow\n    ```\n- Abstract Factory Pattern\n  - Definition: Provides an interface for creating families of related or dependent objects without specifying their concrete classes.\n  - Example:\n    ```python\n    from abc import ABC, abstractmethod\n\n    class AbstractFactory(ABC):\n        @abstractmethod\n        def create_product_a(self):\n            pass\n\n        @abstractmethod\n        def create_product_b(self):\n            pass\n\n    class ConcreteFactory1(AbstractFactory):\n        def create_product_a(self):\n            return ConcreteProductA1()\n\n        def create_product_b(self):\n            return ConcreteProductB1()\n\n    class ConcreteFactory2(AbstractFactory):\n        def create_product_a(self):\n            return ConcreteProductA2()\n\n        def create_product_b(self):\n            return ConcreteProductB2()\n\n    class AbstractProductA(ABC):\n        @abstractmethod\n        def useful_function_a(self):\n            pass\n\n    class ConcreteProductA1(AbstractProductA):\n        def useful_function_a(self):\n            return \"The result of the product A1.\"\n\n    class ConcreteProductA2(AbstractProductA):\n        def useful_function_a(self):\n            return \"The result of the product A2.\"\n\n    class AbstractProductB(ABC):\n        @abstractmethod\n        def useful_function_b(self):\n            pass\n\n        @abstractmethod\n        def another_useful_function_b(self, collaborator):\n            pass\n\n    class ConcreteProductB1(AbstractProductB):\n        def useful_function_b(self):\n            return \"The result of the product B1.\"\n\n        def another_useful_function_b(self, collaborator):\n            return f\"The result of the B1 collaborating with {collaborator.useful_function_a()}\"\n\n    class ConcreteProductB2(AbstractProductB):\n        def useful_function_b(self):\n            return \"The result of the product B2.\"\n\n        def another_useful_function_b(self, collaborator):\n            return f\"The result of the B2 collaborating with {collaborator.useful_function_a()}\"\n\n    def client_code(factory: AbstractFactory) -\u003e None:\n        product_a = factory.create_product_a()\n        product_b = factory.create_product_b()\n\n        print(product_b.useful_function_b())\n        print(product_b.another_useful_function_b(product_a))\n\n\n    client_code(ConcreteFactory1())  # Output: The result of the product B1. The result of the B1 collaborating with The result of the product A1.\n    client_code(ConcreteFactory2())  # Output: The result of the product B2. The result of the B2 collaborating with The result of the product A2.\n    ```\n\n- Builder Pattern\n  - Definition: Separates the construction of a complex object from its representation so that the same construction process can create different representations.\n  - Example:\n    ```python\n    from abc import ABC, abstractmethod\n\n    class Builder(ABC):\n        @abstractmethod\n        def build_part_a(self):\n            pass\n\n        @abstractmethod\n        def build_part_b(self):\n            pass\n\n    class ConcreteBuilder1(Builder):\n        def __init__(self):\n            self.product = Product()\n\n        def build_part_a(self):\n            self.product.add(\"PartA1\")\n\n        def build_part_b(self):\n            self.product.add(\"PartB1\")\n\n    class ConcreteBuilder2(Builder):\n        def __init__(self):\n            self.product = Product()\n\n        def build_part_a(self):\n            self.product.add(\"PartA2\")\n\n        def build_part_b(self):\n            self.product.add(\"PartB2\")\n\n    class Product:\n        def __init__(self):\n            self.parts = []\n\n        def add(self, part):\n            self.parts.append(part)\n\n        def list_parts(self):\n            print(f\"Product parts: {', '.join(self.parts)}\")\n\n    class Director:\n        def __init__(self):\n            self.builder = None\n\n        def set_builder(self, builder):\n            self.builder = builder\n\n        def build_minimal_viable_product(self):\n            self.builder.build_part_a()\n\n        def build_full_featured_product(self):\n            self.builder.build_part_a()\n            self.builder.build_part_b()\n\n    director = Director()\n\n    builder1 = ConcreteBuilder1()\n    director.set_builder(builder1)\n    director.build_minimal_viable_product()\n    builder1.product.list_parts()  # Output: Product parts: PartA1\n\n    builder2 = ConcreteBuilder2()\n    director.set_builder(builder2)\n    director.build_full_featured_product()\n    builder2.product.list_parts()  # Output: Product parts: PartA2, PartB2\n    ```\n- Prototype Pattern\n  - Definition: Creates new objects by copying an existing object, known as the prototype.\n  - Example:\n    ```python\n    import copy\n\n    class Prototype:\n        def __init__(self):\n            self._objects = {}\n\n        def register_object(self, name, obj):\n            self._objects[name] = obj\n\n        def unregister_object(self, name):\n            del self._objects[name]\n\n        def clone(self, name, **attrs):\n            obj = copy.deepcopy(self._objects.get(name))\n            obj.__dict__.update(attrs)\n            return obj\n\n    class Car:\n        def __init__(self):\n            self.make = \"Toyota\"\n            self.model = \"Corolla\"\n            self.year = 2022\n\n        def __str__(self):\n            return f\"{self.year} {self.make} {self.model}\"\n\n    car_prototype = Prototype()\n    car_prototype.register_object(\"Corolla\", Car())\n\n    car1 = car_prototype.clone(\"Corolla\")\n    print(car1)  # Output: 2022 Toyota Corolla\n\n    car2 = car_prototype.clone(\"Corolla\", year=2023)\n    print(car2)  # Output: 2023 Toyota Corolla\n    ```\n\n## Structural Patterns\n- Adapter Pattern\n  - Definition: Allows objects with incompatible interfaces to collaborate.\n  - Example:\n    ```python\n    class Target:\n        def request(self):\n            return \"Target: The default target's behavior.\"\n\n    class Adaptee:\n        def specific_request(self):\n            return \".eetpadA eht fo roivaheb laicepS\"\n\n    class Adapter(Target):\n        def __init__(self, adaptee):\n            self.adaptee = adaptee\n\n        def request(self):\n            return f\"Adapter: (TRANSLATED) {self.adaptee.specific_request()[::-1]}\"\n\n    def client_code(target):\n        print(target.request())\n\n    adaptee = Adaptee()\n    adapter = Adapter(adaptee)\n\n    client_code(adapter)  # Output: Adapter: (TRANSLATED) Special behavior of the adaptee.\n    ```\n\n- Bridge Pattern\n  - Definition: Decouples an abstraction from its implementation so that the two can vary independently.\n  - Example:\n    ```python\n    from abc import ABC, abstractmethod\n\n    class Implementor(ABC):\n        @abstractmethod\n        def operation_implementation(self) -\u003e str:\n            pass\n\n    class ConcreteImplementorA(Implementor):\n        def operation_implementation(self) -\u003e str:\n            return \"ConcreteImplementorA: Here's the result on the platform A.\"\n\n    class ConcreteImplementorB(Implementor):\n        def operation_implementation(self) -\u003e str:\n            return \"ConcreteImplementorB: Here's the result on the platform B.\"\n\n    class Abstraction:\n        def __init__(self, implementor: Implementor):\n            self.implementor = implementor\n\n        def operation(self) -\u003e str:\n            return (f\"Abstraction: Base operation with:\\n\"\n                    f\"{self.implementor.operation_implementation()}\")\n\n    class ExtendedAbstraction(Abstraction):\n        def operation(self) -\u003e str:\n            return (f\"ExtendedAbstraction: Extended operation with:\\n\"\n                    f\"{self.implementor.operation_implementation()}\")\n\n    def client_code(abstraction: Abstraction) -\u003e None:\n        print(abstraction.operation())\n\n    implementor_a = ConcreteImplementorA()\n    implementor_b = ConcreteImplementorB()\n\n    abstraction_a = Abstraction(implementor_a)\n    abstraction_b = ExtendedAbstraction(implementor_b)\n\n    client_code(abstraction_a)  # Output: Abstraction: Base operation with: ConcreteImplementorA: Here's the result on the platform A.\n    client_code(abstraction_b)  # Output: ExtendedAbstraction: Extended operation with: ConcreteImplementorB: Here's the result on the platform B.\n    ```\n- Composite Pattern\n  - Definition: Composes objects into tree structures to represent part-whole hierarchies. Allows clients to treat individual objects and compositions of objects uniformly.\n  - Example:\n    ```python\n    from abc import ABC, abstractmethod\n\n    class Component(ABC):\n        @abstractmethod\n        def operation(self) -\u003e str:\n            pass\n\n    class Leaf(Component):\n        def operation(self) -\u003e str:\n            return \"Leaf\"\n\n    class Composite(Component):\n        def __init__(self):\n            self._children = []\n\n        def add(self, component: Component) -\u003e None:\n            self._children.append(component)\n\n        def remove(self, component: Component) -\u003e None:\n            self._children.remove(component)\n\n        def operation(self) -\u003e str:\n            results = []\n            for child in self._children:\n                results.append(child.operation())\n            return f\"Branch({'+'.join(results)})\"\n\n    def client_code(component: Component) -\u003e None:\n        print(f\"RESULT: {component.operation()}\", end=\"\")\n\n    leaf = Leaf()\n    print(\"Client: I've got a simple component:\")\n    client_code(leaf)  # Output: Client: I've got a simple component: RESULT: Leaf\n\n    composite = Composite()\n    tree = Composite()\n\n    tree.add(leaf)\n    tree.add(composite)\n    print(\"\\n\\nClient: Now I've got a composite tree:\")\n    client_code(tree)  # Output: Client: Now I've got a composite tree: RESULT: Leaf+Branch()\n\n    branch1 = Composite()\n    branch1.add(Leaf())\n    branch1.add(Leaf())\n\n    branch2 = Composite()\n    branch2.add(Leaf())\n\n    tree.add(branch1)\n    tree.add(branch2)\n\n    print(\"\\n\\nClient: Now I've got a more complex composite tree:\")\n    client_code(tree)  # Output: Client: Now I've got a more complex composite tree: RESULT: Leaf+Branch(Leaf+Leaf)+Branch(Leaf)\n    ```\n\n- Decorator Pattern\n  - Definition: Attaches additional responsibilities to an object dynamically. Provides a flexible alternative to subclassing for extending functionality.\n  - Example:\n    ```python\n    from abc import ABC, abstractmethod\n\n    class Component(ABC):\n        @abstractmethod\n        def operation(self) -\u003e str:\n            pass\n\n    class ConcreteComponent(Component):\n        def operation(self) -\u003e str:\n            return \"ConcreteComponent\"\n\n    class Decorator(Component):\n        def __init__(self, component: Component) -\u003e None:\n            self._component = component\n\n        @abstractmethod\n        def operation(self) -\u003e str:\n            pass\n\n    class ConcreteDecoratorA(Decorator):\n        def operation(self) -\u003e str:\n            return f\"ConcreteDecoratorA({self._component.operation()})\"\n\n    class ConcreteDecoratorB(Decorator):\n        def operation(self) -\u003e str:\n            return f\"ConcreteDecoratorB({self._component.operation()})\"\n\n    def client_code(component: Component) -\u003e None:\n        print(f\"RESULT: {component.operation()}\", end=\"\")\n\n    simple = ConcreteComponent()\n    print(\"Client: I've got a simple component:\")\n    client_code(simple)  # Output: Client: I've got a simple component: RESULT: ConcreteComponent\n\n    decorator1 = ConcreteDecoratorA(simple)\n    decorator2 = ConcreteDecoratorB(decorator1)\n    print(\"\\n\\nClient: Now I've got a decorated component:\")\n    client_code(decorator2)  # Output: Client: Now I've got a decorated component: RESULT: ConcreteDecoratorB(ConcreteDecoratorA(ConcreteComponent))\n    ```\n\n- Facade Pattern\n  - Definition: Provides a simplified interface to a complex system of classes, interfaces, or subsystems.\n  - Example:\n    ```python\n    class Subsystem1:\n        def operation1(self) -\u003e str:\n            return \"Subsystem1: Ready!\"\n\n        def operation_n(self) -\u003e str:\n            return \"Subsystem1: Go!\"\n\n    class Subsystem2:\n        def operation1(self) -\u003e str:\n            return \"Subsystem2: Get ready!\"\n\n        def operation_z(self) -\u003e str:\n            return \"Subsystem2: Fire!\"\n\n    class Facade:\n        def __init__(self, subsystem1: Subsystem1, subsystem2: Subsystem2):\n            self._subsystem1 = subsystem1\n            self._subsystem2 = subsystem2\n\n        def operation(self) -\u003e str:\n            results = []\n            results.append(\"Facade initializes subsystems:\")\n            results.append(self._subsystem1.operation1())\n            results.append(self._subsystem2.operation1())\n            results.append(\"Facade orders subsystems to perform the action:\")\n            results.append(self._subsystem1.operation_n())\n            results.append(self._subsystem2.operation_z())\n            return \"\\n\".join(results)\n\n    def client_code(facade: Facade) -\u003e None:\n        print(facade.operation())\n\n    subsystem1 = Subsystem1()\n    subsystem2 = Subsystem2()\n    facade = Facade(subsystem1, subsystem2)\n\n    client_code(facade)\n    ```\n\n- Proxy Pattern\n  - Definition: Provides a surrogate or placeholder for another object to control access to it.\n  - Example:\n    ```python\n    from abc import ABC, abstractmethod\n\n    class Subject(ABC):\n        @abstractmethod\n        def request(self) -\u003e str:\n            pass\n\n    class RealSubject(Subject):\n        def request(self) -\u003e str:\n            return \"RealSubject: Handling request.\"\n\n    class Proxy(Subject):\n        def __init__(self, real_subject: RealSubject) -\u003e None:\n            self._real_subject = real_subject\n\n        def request(self) -\u003e str:\n            if self.check_access():\n                return self._real_subject.request()\n            return \"Proxy: Access denied.\"\n\n        def check_access(self) -\u003e bool:\n            return True  # For simplicity, always grant access.\n\n    def client_code(subject: Subject) -\u003e None:\n        print(subject.request())\n\n    real_subject = RealSubject()\n    proxy = Proxy(real_subject)\n\n    print(\"Client: Executing the client code with a real subject:\")\n    client_code(real_subject)  # Output: Client: Executing the client code with a real subject: RealSubject: Handling request.\n\n    print(\"\\nClient: Executing the same client code with a proxy:\")\n    client_code(proxy)  # Output: Client: Executing the same client code with a proxy: RealSubject: Handling request.\n    ```\n\n## Behavioral Patterns\n\n- Observer Pattern\n  - Definition: Defines a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.\n  - Example:\n    ```python\n    class Observer:\n        def update(self, subject):\n            pass\n\n    class Subject:\n        def __init__(self):\n            self._observers = []\n\n        def attach(self, observer):\n            self._observers.append(observer)\n\n        def detach(self, observer):\n            self._observers.remove(observer)\n\n        def notify(self):\n            for observer in self._observers:\n                observer.update(self)\n\n        def some_business_logic(self):\n            self.notify()\n\n    class ConcreteObserverA(Observer):\n        def update(self, subject):\n            if subject.some_business_logic():\n                print(\"ConcreteObserverA: Reacted to the event\")\n\n    class ConcreteObserverB(Observer):\n        def update(self, subject):\n            if subject.some_business_logic():\n                print(\"ConcreteObserverB: Reacted to the event\")\n\n    subject = Subject()\n    observer1 = ConcreteObserverA()\n    observer2 = ConcreteObserverB()\n\n    subject.attach(observer1)\n    subject.attach(observer2)\n\n    subject.some_business_logic()\n    ```\n\n- Strategy Pattern\n  - Definition: Defines a family of algorithms, encapsulates each one, and makes them interchangeable. Strategy lets the algorithm vary independently from clients that use it.\n  - Example:\n    ```python\n    from abc import ABC, abstractmethod\n\n    class Strategy(ABC):\n        @abstractmethod\n        def do_algorithm(self, data):\n            pass\n\n    class ConcreteStrategyA(Strategy):\n        def do_algorithm(self, data):\n            return sorted(data)\n\n    class ConcreteStrategyB(Strategy):\n        def do_algorithm(self, data):\n            return reversed(sorted(data))\n\n    class Context:\n        def __init__(self, strategy: Strategy):\n            self._strategy = strategy\n\n        def context_interface(self, data):\n            return self._strategy.do_algorithm(data)\n\n    context = Context(ConcreteStrategyA())\n    result = context.context_interface([3, 2, 1])\n    print(result)  # Output: [1, 2, 3]\n\n    context = Context(ConcreteStrategyB())\n    result = context.context_interface([3, 2, 1])\n    print(result)  # Output: [3, 2, 1]\n    ```\n\n- Chain of Responsibility Pattern\n  - Definition: Allows passing requests along a chain of handlers. Upon receiving a request, each handler decides either to process the request or to pass it to the next handler in the chain.\n  - Example:\n    ```python\n    from abc import ABC, abstractmethod\n\n    class Handler(ABC):\n        @abstractmethod\n        def set_next(self, handler):\n            pass\n\n        @abstractmethod\n        def handle(self, request):\n            pass\n\n    class AbstractHandler(Handler):\n        _next_handler = None\n\n        def set_next(self, handler):\n            self._next_handler = handler\n            return handler\n\n        @abstractmethod\n        def handle(self, request):\n            if self._next_handler:\n                return self._next_handler.handle(request)\n            return None\n\n    class ConcreteHandler1(AbstractHandler):\n        def handle(self, request):\n            if request == \"Handler1\":\n                return f\"{self.__class__.__name__}: Handled {request}\"\n            else:\n                return super().handle(request)\n\n    class ConcreteHandler2(AbstractHandler):\n        def handle(self, request):\n            if request == \"Handler2\":\n                return f\"{self.__class__.__name__}: Handled {request}\"\n            else:\n                return super().handle(request)\n\n    handler1 = ConcreteHandler1()\n    handler2 = ConcreteHandler2()\n\n    handler1.set_next(handler2)\n    result = handler1.handle(\"Handler1\")\n    print(result)  # Output: ConcreteHandler1: Handled Handler1\n\n    result = handler1.handle(\"Handler2\")\n    print(result)  # Output: ConcreteHandler2: Handled Handler2\n    ```\n\n- Command Pattern\n  - Definition: Turns a request into a stand-alone object that contains all information about the request. This transformation lets you pass requests as arguments, delay or queue a request's\n    ```python\n    from abc import ABC, abstractmethod\n\n    class Command(ABC):\n        @abstractmethod\n        def execute(self):\n            pass\n\n    class Receiver:\n        def action(self):\n            return \"Receiver: executing action.\"\n\n    class ConcreteCommand(Command):\n        def __init__(self, receiver):\n            self._receiver = receiver\n\n        def execute(self):\n            return self._receiver.action()\n\n    class Invoker:\n        def __init__(self):\n            self._command = None\n\n        def set_command(self, command):\n            self._command = command\n\n        def execute_command(self):\n            return self._command.execute()\n\n    receiver = Receiver()\n    command = ConcreteCommand(receiver)\n    invoker = Invoker()\n\n    invoker.set_command(command)\n    result = invoker.execute_command()\n    print(result)  # Output: Receiver: executing action.\n    ```\n- State Pattern\n  - Definition: Allows an object to alter its behavior when its internal state changes. The object will appear to change its class.\n    Example:\n    ```python\n    from abc import ABC, abstractmethod\n\n    class Context:\n        _state = None\n\n        def __init__(self, state):\n            self.transition_to(state)\n\n        def transition_to(self, state):\n            print(f\"Context: Transition to {type(state).__name__}\")\n            self._state = state\n            self._state.context = self\n\n        def request(self):\n            self._state.handle()\n\n    class State(ABC):\n        @property\n        def context(self):\n            return self._context\n\n        @context.setter\n        def context(self, context):\n            self._context = context\n\n        @abstractmethod\n        def handle(self):\n            pass\n\n    class ConcreteStateA(State):\n        def handle(self):\n            print(\"ConcreteStateA: Handle request\")\n\n    class ConcreteStateB(State):\n        def handle(self):\n            print(\"ConcreteStateB: Handle request\")\n\n    context = Context(ConcreteStateA())\n    context.request()  # Output: Context: Transition to ConcreteStateA, ConcreteStateA: Handle request\n\n    context.transition_to(ConcreteStateB())\n    context.request()  # Output: Context: Transition to ConcreteStateB, ConcreteStateB: Handle request\n    ```\n\n- Template Method Pattern\n  - Definition: Defines the skeleton of an algorithm in the superclass but lets subclasses override specific steps of the algorithm without changing its structure.\n    Example:\n    ```python\n    from abc import ABC, abstractmethod\n\n    class AbstractClass(ABC):\n        def template_method(self):\n            result = []\n            result.append(self.base_operation1())\n            result.append(self.required_operations1())\n            result.append(self.base_operation2())\n            result.append(self.hook1())\n            if self.hook2():\n                result.append(self.required_operations2())\n            return \"\\n\".join(result)\n\n        def base_operation1(self):\n            return \"AbstractClass: base operation1\"\n\n        @abstractmethod\n        def required_operations1(self):\n            pass\n\n        def base_operation2(self):\n            return \"AbstractClass: base operation2\"\n\n        def hook1(self):\n            return \"AbstractClass: hook1\"\n\n        def hook2(self):\n            return True\n\n        @abstractmethod\n        def required_operations2(self):\n            pass\n\n    class ConcreteClass1(AbstractClass):\n        def required_operations1(self):\n            return \"ConcreteClass1: implementing required operation1\"\n\n        def required_operations2(self):\n            return \"ConcreteClass1: implementing required operation2\"\n\n    class ConcreteClass2(AbstractClass):\n        def required_operations1(self):\n            return \"ConcreteClass2: implementing required operation1\"\n\n        def hook2(self):\n            return False\n\n        def required_operations2(self):\n            return \"ConcreteClass2: implementing required operation2\"\n\n    def client_code(abstract_class):\n        print(abstract_class.template_method())\n\n    concrete_class1 = ConcreteClass1()\n    print(\"Client: ConcreteClass1 calls the template method:\")\n    client_code(concrete_class1)\n\n    concrete_class2 = ConcreteClass2()\n    print(\"\\nClient: ConcreteClass2 calls the template method:\")\n    client_code(concrete_class2)\n    ```\n\n## Best Practices and Considerations\n  - When to Apply SOLID Principles\n  - Choosing the Right Design Pattern\n  - Trade-offs and Caveats\n  - Refactoring Legacy Code\n  - Testing Strategies\n\n## Resources\n  - Books on SOLID Principles and Design Patterns\n  - Online Courses and Tutorials\n  - Design Pattern Catalogs\n  - Code Examples and Implementations\n  - Community Forums and Discussions\n\n## Conclusion\n  - Practice Implementing SOLID Principles and Design Patterns\n  - Continuously Refactor and Improve Code\n  - Share Knowledge and Learn from Others\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsanix-darker%2Fsolid_learn","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsanix-darker%2Fsolid_learn","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsanix-darker%2Fsolid_learn/lists"}