{"id":20495507,"url":"https://github.com/vyahello/python-ood","last_synced_at":"2025-09-07T12:40:45.560Z","repository":{"id":39737568,"uuid":"139270499","full_name":"vyahello/python-ood","owner":"vyahello","description":"💠 Essential object oriented design (python, pytest, travisCI)","archived":false,"fork":false,"pushed_at":"2024-12-27T12:31:01.000Z","size":939,"stargazers_count":58,"open_issues_count":7,"forks_count":9,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-03T12:12:22.316Z","etag":null,"topics":["design-patterns","object-oriented-design","object-oriented-programming","ood","pytest","python"],"latest_commit_sha":null,"homepage":"https://vyahello.github.io/python-ood","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/vyahello.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.md","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":"2018-06-30T18:16:28.000Z","updated_at":"2025-01-12T18:29:44.000Z","dependencies_parsed_at":"2025-03-02T13:10:50.586Z","dependency_job_id":"9fc28eb7-d5c0-4afe-9ac2-24eaf1a8b0d6","html_url":"https://github.com/vyahello/python-ood","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/vyahello/python-ood","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vyahello%2Fpython-ood","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vyahello%2Fpython-ood/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vyahello%2Fpython-ood/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vyahello%2Fpython-ood/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/vyahello","download_url":"https://codeload.github.com/vyahello/python-ood/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vyahello%2Fpython-ood/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":274039532,"owners_count":25211901,"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","status":"online","status_checked_at":"2025-09-07T02:00:09.463Z","response_time":67,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["design-patterns","object-oriented-design","object-oriented-programming","ood","pytest","python"],"created_at":"2024-11-15T17:46:14.661Z","updated_at":"2025-09-07T12:40:45.517Z","avatar_url":"https://github.com/vyahello.png","language":"Python","readme":"![Screenshot](logo.png)\n\n[![made-with-python](https://img.shields.io/badge/Made%20with-Python-1f425f.svg)](https://www.python.org/)\n[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)\n[![Checked with pylint](https://img.shields.io/badge/pylint-checked-blue)](https://www.pylint.org)\n[![Build Status](https://www.travis-ci.com/vyahello/python-ood.svg?branch=master)](https://www.travis-ci.com/github/vyahello/python-ood)\n[![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE.md)\n[![CodeFactor](https://www.codefactor.io/repository/github/vyahello/python-ood/badge)](https://www.codefactor.io/repository/github/vyahello/python-ood)\n[![EO principles respected here](https://www.elegantobjects.org/badge.svg)](https://www.elegantobjects.org)\n[![Docs](https://img.shields.io/badge/docs-github-orange)](https://vyahello.github.io/python-ood)\n\n# Python OOD\n\u003e The project describes the architecture of the most useful python object oriented design patterns.\n\n## Tools\n\n### Language(s)\n- python 3.9, 3.10\n\n### Development\n- [pylint](https://www.pylint.org/) code analyser\n- [black](https://black.readthedocs.io/en/stable/) code formatter\n- [travis](https://travis-ci.org/) CI\n- [pytest](https://docs.pytest.org/en/stable/) framework\n\n## Table of contents\n- [Creational](#creational)\n  - [Factory method](#factory-method)\n  - [Abstract factory](#abstract-factory)\n  - [Singleton](#singleton)\n  - [Builder](#builder)\n  - [Prototype](#prototype)\n- [Structural](#structural)\n  - [Decorator](#decorator)\n  - [Proxy](#proxy)\n  - [Adapter](#adapter)\n  - [Composite](#composite)\n  - [Bridge](#bridge)\n  - [Facade](#facade)\n- [Behavioral](#behavioral)\n  - [MVC](#mvc)\n  - [Observer](#observer)\n  - [Visitor](#visitor)\n  - [Iterator](#iterator)\n  - [Strategy](#strategy)\n  - [Chain of responsibility](#chain-of-responsibility)\n- [Development notes](#development-notes)\n  - [Code analysis](#code-analysis)\n  - [Release notes](#release-notes)\n  - [Meta](#meta)\n  - [Contributing](#contributing)\n\n## Creational\nCreational types of patterns used to create objects in a systematic way. Supports flexibility and different subtypes of objects from the same class at runtime. \nHere **polymorphism** is often used.\n\n### Factory method\nFactory method defines an interface for creating an object but defers object instantiation to run time.\n\n```python\nfrom abc import ABC, abstractmethod\n\n\nclass Shape(ABC):\n    \"\"\"Defines a shape interface.\"\"\"\n\n    @abstractmethod\n    def draw(self) -\u003e str:\n        \"\"\"Draws a shape.\"\"\"\n        pass\n\n\nclass ShapeError(Exception):\n    \"\"\"Represents shape error message.\"\"\"\n\n    pass\n\n\nclass Circle(Shape):\n    \"\"\"A shape subclass.\"\"\"\n\n    def draw(self) -\u003e str:\n        \"\"\"Draws a circle.\"\"\"\n        return \"Circle.draw\"\n\n\nclass Square(Shape):\n    \"\"\"A shape subclass.\"\"\"\n\n    def draw(self) -\u003e str:\n        \"\"\"Draws a square.\"\"\"\n        return \"Square.draw\"\n\n\nclass ShapeFactory:\n    \"\"\"A shape factory.\"\"\"\n\n    def __init__(self, shape: str) -\u003e None:\n        self._shape: str = shape\n\n    def shape(self) -\u003e Shape:\n        \"\"\"Returns a shape.\"\"\"\n        if self._shape == \"circle\":\n            return Circle()\n        if self._shape == \"square\":\n            return Square()\n        raise ShapeError(f'Could not find \"{self._shape}\" shape!')\n\n\n# circle shape\nfactory: ShapeFactory = ShapeFactory(shape=\"circle\")\ncircle: Shape = factory.shape()\nprint(circle.__class__.__name__)\nprint(circle.draw())\n\n# square shape\nfactory: ShapeFactory = ShapeFactory(shape=\"square\")\nsquare: Shape = factory.shape()\nprint(square.__class__.__name__)\nprint(square.draw())\n```\n\nFactory encapsulates objects creation. Factory is an object that is specialized in creation of other objects. \n- Benefits:  \n  - Useful when you are not sure what kind of object you will be needed eventually.\n  - Application need to decide what class it has to use.\n- Exercise:\n  - Pet shop is selling dogs but now it sells cats too.\n\n```python\nfrom abc import ABC, abstractmethod\n\n\nclass Pet(ABC):\n    \"\"\"Abstraction of a pet.\"\"\"\n\n    @abstractmethod\n    def speak(self) -\u003e str:\n        \"\"\"Interface for a pet to speak.\"\"\"\n        pass\n\n\nclass Dog(Pet):\n    \"\"\"A simple dog class.\"\"\"\n\n    def __init__(self, name: str) -\u003e None:\n        self._dog_name: str = name\n\n    def speak(self) -\u003e str:\n        return f\"{self._dog_name} says Woof!\"\n\n\nclass Cat(Pet):\n    \"\"\"A simple cat class.\"\"\"\n\n    def __init__(self, name: str) -\u003e None:\n        self._cat_name: str = name\n\n    def speak(self) -\u003e str:\n        return f\"{self._cat_name} says Meow!\"\n\n\ndef get_pet(pet: str) -\u003e Pet:\n    \"\"\"The factory method.\"\"\"\n    return {\"dog\": Dog(\"Hope\"), \"cat\": Cat(\"Faith\")}[pet]\n\n\n# returns Cat class object\nget_pet(\"cat\")\n```\n\n**[⬆ back to top](#table-of-contents)**\n\n### Abstract factory\nIn abstract factory a client expects to receive family related objects. But don't have to know which family it is until run time. Abstract factory is related to factory method and concrete product are singletons.\n- Implementation idea:\n  - Abstract factory: pet factory\n  - Concrete factory: dog factory and cat factory\n  - Abstract product\n  - Concrete product: dog and dog food, cat and cat food\n- Exercise:\n  - We have a Pet factory (which includes Dog and Cat factory and both factories produced related products such as Dog and Cat food and we have a PetFactory which gets Cat or Dog factory).\n\n```python\nfrom abc import ABC, abstractmethod\nfrom typing import Generator\n\n\nclass Pet(ABC):\n    \"\"\"Abstract interface of a pet.\"\"\"\n\n    @abstractmethod\n    def speak(self) -\u003e str:\n        pass\n\n    @abstractmethod\n    def type(self) -\u003e str:\n        pass\n\n\nclass Food(ABC):\n    \"\"\"Abstract interface of a food.\"\"\"\n\n    @abstractmethod\n    def show(self) -\u003e str:\n        pass\n\n\nclass PetFactory(ABC):\n    \"\"\"Abstract interface of a pet factory.\"\"\"\n\n    @abstractmethod\n    def pet(self) -\u003e Pet:\n        pass\n\n    @abstractmethod\n    def food(self) -\u003e Food:\n        pass\n\n\nclass PetStore(ABC):\n    \"\"\"Abstract interface of a pet store.\"\"\"\n\n    @abstractmethod\n    def show_pet(self) -\u003e str:\n        pass\n\n\nclass Dog(Pet):\n    \"\"\"A dog pet.\"\"\"\n\n    def __init__(self, name: str, type_: str) -\u003e None:\n        self._name: str = name\n        self._type: str = type_\n\n    def speak(self) -\u003e str:\n        return f'\"{self._name}\" says Woof!'\n\n    def type(self) -\u003e str:\n        return f\"{self._type} dog\"\n\n\nclass DogFood(Food):\n    \"\"\"A dog food.\"\"\"\n\n    def show(self) -\u003e str:\n        return \"Pedigree\"\n\n\nclass DogFactory(PetFactory):\n    \"\"\"A dog factory.\"\"\"\n\n    def __init__(self) -\u003e None:\n        self._dog: Pet = Dog(name=\"Spike\", type_=\"bulldog\")\n        self._food: Food = DogFood()\n\n    def pet(self) -\u003e Pet:\n        return self._dog\n\n    def food(self) -\u003e Food:\n        return self._food\n\n\nclass Cat(Pet):\n    \"\"\"A cat pet.\"\"\"\n\n    def __init__(self, name: str, type_: str) -\u003e None:\n        self._name: str = name\n        self._type: str = type_\n\n    def speak(self) -\u003e str:\n        return f'\"{self._name}\" says Moew!'\n\n    def type(self) -\u003e str:\n        return f\"{self._type} cat\"\n\n\nclass CatFood(Food):\n    \"\"\"A cat food.\"\"\"\n\n    def show(self) -\u003e str:\n        return \"Whiskas\"\n\n\nclass CatFactory(PetFactory):\n    \"\"\"A dog factory.\"\"\"\n\n    def __init__(self) -\u003e None:\n        self._cat: Pet = Cat(name=\"Hope\", type_=\"persian\")\n        self._food: Food = CatFood()\n\n    def pet(self) -\u003e Pet:\n        return self._cat\n\n    def food(self) -\u003e Food:\n        return self._food\n\n\nclass FluffyStore(PetStore):\n    \"\"\"Houses our abstract pet factory.\"\"\"\n\n    def __init__(self, pet_factory: PetFactory) -\u003e None:\n        self._pet: Pet = pet_factory.pet()\n        self._pet_food: Food = pet_factory.food()\n\n    def show_pet(self) -\u003e Generator[str, None, None]:\n        yield f\"Our pet is {self._pet.type()}\"\n        yield f\"{self._pet.type()} {self._pet.speak()}\"\n        yield f\"It eats {self._pet_food.show()} food\"\n\n\n\n# cat factory\ncat_factory: PetFactory = CatFactory()\nstore: PetStore = FluffyStore(cat_factory)\nprint(tuple(store.show_pet()))\n\n# dog factory\ndog_factory: PetFactory = DogFactory()\nstore: PetStore = FluffyStore(dog_factory)\nprint(tuple(store.show_pet()))\n```\n\n**[⬆ back to top](#table-of-contents)**\n\n### Singleton\nPython has global variables and modules which are **_singletons_**. Singleton allows only one object to be instantiated from a class template.\nUseful if you want to share cached information to multiple objects.\n\n**Classic singleton**\n\n```python\nfrom typing import Any, Dict\n\n\nclass SingletonMeta(type):\n    \"\"\"Singleton metaclass implementation.\"\"\"\n\n    def __init__(cls, cls_name: str, bases: tuple, namespace: dict):\n        cls.__instance = None\n        super().__init__(cls_name, bases, namespace)\n\n    def __call__(cls, *args, **kwargs):\n        if cls.__instance is None:\n            cls.__instance = super().__call__(*args, **kwargs)\n            return cls.__instance\n        return cls.__instance\n\n\nclass Singleton:\n    \"\"\"Makes all instances as the same object.\"\"\"\n\n    def __new__(cls) -\u003e \"Singleton\":\n        if not hasattr(cls, \"_instance\"):\n            cls._instance = super().__new__(cls)\n        return cls._instance\n\n\ndef singleton(cls: Any) -\u003e Any:\n    \"\"\"A singleton decorator.\"\"\"\n    instances: Dict[Any, Any] = {}\n\n    def get_instance() -\u003e Any:\n        if cls not in instances:\n            instances[cls] = cls()\n        return instances[cls]\n\n    return get_instance\n\n\n@singleton\nclass Bar:\n    \"\"\"A fancy object.\"\"\"\n\n    pass\n\n\nsingleton_one: Singleton = Singleton()\nsingleton_two: Singleton = Singleton()\n\nprint(id(singleton_one))\nprint(id(singleton_two))\nprint(singleton_one is singleton_two)\n\nbar_one: Bar = Bar()\nbar_two: Bar = Bar()\nprint(id(bar_one))\nprint(id(bar_two))\nprint(bar_one is bar_two)\n```\n\n**Borg singleton**\n\n```python\nfrom typing import Dict, Any\n\n\nclass Borg:\n    \"\"\"Borg class making class attributes global.\n    Safe the same state of all instances but instances are all different.\"\"\"\n\n    _shared_state: Dict[Any, Any] = {}\n\n    def __init__(self) -\u003e None:\n        self.__dict__ = self._shared_state\n\n\nclass BorgSingleton(Borg):\n    \"\"\"This class shares all its attribute among its instances. Store the same state.\"\"\"\n\n    def __init__(self, **kwargs: Any) -\u003e None:\n        Borg.__init__(self)\n        self._shared_state.update(kwargs)\n\n    def __str__(self) -\u003e str:\n        return str(self._shared_state)\n\n\n# Create a singleton object and add out first acronym\nx: Borg = BorgSingleton(HTTP=\"Hyper Text Transfer Protocol\")\nprint(x)\n\n# Create another singleton which will add to the existent dict attribute\ny: Borg = BorgSingleton(SNMP=\"Simple Network Management Protocol\")\nprint(y)\n```\n\n**[⬆ back to top](#table-of-contents)**\n\n### Builder\nBuilder reduces complexity of building objects.\n- Participants:\n  - Director\n  - Abstract Builder: interfaces\n  - Concrete Builder: implements the interfaces\n  - Product: object being built\n- Exercise:\n  - Build a car object\n\n```python\nfrom abc import ABC, abstractmethod\n\n\nclass Machine(ABC):\n    \"\"\"Abstract machine interface.\"\"\"\n\n    @abstractmethod\n    def summary(self) -\u003e str:\n        pass\n\n\nclass Builder(ABC):\n    \"\"\"Abstract builder interface.\"\"\"\n\n    @abstractmethod\n    def add_model(self) -\u003e None:\n        pass\n\n    @abstractmethod\n    def add_tires(self) -\u003e None:\n        pass\n\n    @abstractmethod\n    def add_engine(self) -\u003e None:\n        pass\n\n    @abstractmethod\n    def machine(self) -\u003e Machine:\n        pass\n\n\nclass Car(Machine):\n    \"\"\"A car product.\"\"\"\n\n    def __init__(self) -\u003e None:\n        self.model: str = None\n        self.tires: str = None\n        self.engine: str = None\n\n    def summary(self) -\u003e str:\n        return \"Car details: {} | {} | {}\".format(\n            self.model, self.tires, self.engine\n        )\n\n\nclass SkyLarkBuilder(Builder):\n    \"\"\"Provides parts and tools to work on the car parts.\"\"\"\n\n    def __init__(self) -\u003e None:\n        self._car: Machine = Car()\n\n    def add_model(self) -\u003e None:\n        self._car.model = \"SkyBuilder model\"\n\n    def add_tires(self) -\u003e None:\n        self._car.tires = \"Motosport tires\"\n\n    def add_engine(self) -\u003e None:\n        self._car.engine = \"GM Motors engine\"\n\n    def machine(self) -\u003e Machine:\n        return self._car\n\n\nclass Director:\n    \"\"\"A director. Responsible for `Car` assembling.\"\"\"\n\n    def __init__(self, builder_: Builder) -\u003e None:\n        self._builder: Builder = builder_\n\n    def construct_machine(self) -\u003e None:\n        self._builder.add_model()\n        self._builder.add_tires()\n        self._builder.add_engine()\n\n    def release_machine(self) -\u003e Machine:\n        return self._builder.machine()\n\n\nbuilder: Builder = SkyLarkBuilder()\ndirector: Director = Director(builder)\ndirector.construct_machine()\ncar: Machine = director.release_machine()\nprint(car.summary())\n```\n\n**[⬆ back to top](#table-of-contents)**\n\n### Prototype\nPrototype patterns are related to abstract factory pattern.\n- Ideas:\n  - Clone objects according to prototypical instance.\n  - Creating many identical objects individually.\n  - Clone individual objects\n  - Create a prototypical instance first\n- Exercise:\n  - Use the same car if car has same color or options, you can clone objects instead of creating individual objects\n\n```python\nimport copy\nfrom abc import ABC, abstractmethod\nfrom typing import Dict, Any\n\n\nclass Machine(ABC):\n    \"\"\"Abstract machine interface.\"\"\"\n\n    @abstractmethod\n    def summary(self) -\u003e str:\n        pass\n\n\nclass Car(Machine):\n    \"\"\"A car object.\"\"\"\n\n    def __init__(self) -\u003e None:\n        self._name: str = \"Skylar\"\n        self._color: str = \"Red\"\n        self._options: str = \"Ex\"\n\n    def summary(self) -\u003e str:\n        return \"Car details: {} | {} | {}\".format(\n            self._name, self._color, self._options\n        )\n\n\nclass Prototype:\n    \"\"\"A prototype object.\"\"\"\n\n    def __init__(self) -\u003e None:\n        self._elements: Dict[Any, Any] = {}\n\n    def register_object(self, name: str, machine: Machine) -\u003e None:\n        self._elements[name] = machine\n\n    def unregister_object(self, name: str) -\u003e None:\n        del self._elements[name]\n\n    def clone(self, name: str, **attr: Any) -\u003e Car:\n        obj: Any = copy.deepcopy(self._elements[name])\n        obj.__dict__.update(attr)\n        return obj\n\n\n# prototypical car object to be cloned\nprimary_car: Machine = Car()\nprint(primary_car.summary())\nprototype: Prototype = Prototype()\nprototype.register_object(\"skylark\", primary_car)\n\n# clone a car object\ncloned_car: Machine = prototype.clone(\"skylark\")\nprint(cloned_car.summary())\n```\n\n**[⬆ back to top](#table-of-contents)**\n\n## Structural\nStructural type of patterns establish useful relationships between software components. Here **_inheritance_** is often used.\n- Ideas:\n  - Route maps the user request to a `Controller` which...\n  - Uses the `Model` to retrieve all of the necessary data, organizes it and send it off to the...\n  - View, which then uses that data to render the web page\n\n**[⬆ back to top](#table-of-contents)**\n\n### MVC\nMVC (Model-View-Controller) is a UI pattern intended to separate internal representation of data from ways it is presented to/from the user.\n\n```python\nfrom abc import ABC, abstractmethod\nfrom typing import List, Dict, Iterator, Any\n\n\nclass Model(ABC):\n    \"\"\"Abstract model defines interfaces.\"\"\"\n\n    @abstractmethod\n    def __iter__(self) -\u003e Iterator[str]:\n        pass\n\n    @abstractmethod\n    def get(self, item: str) -\u003e Dict[str, int]:\n        \"\"\"Returns an object with a .items() call method\n        that iterates over key,value pairs of its information.\"\"\"\n        pass\n\n    @property\n    @abstractmethod\n    def item_type(self) -\u003e str:\n        pass\n\n\nclass View(ABC):\n    \"\"\"Abstract view defines interfaces.\"\"\"\n\n    @abstractmethod\n    def show_item_list(self, item_type: str, item_list: List[str]) -\u003e None:\n        pass\n\n    @abstractmethod\n    def show_item_information(\n        self, item_type: str, item_name: str, item_info: List[str]\n    ) -\u003e None:\n        \"\"\"\n        Will look for item information by iterating \n        over key,value pairs yielded by item_info.items().\n        \"\"\"\n        pass\n\n    @abstractmethod\n    def item_not_found(self, item_type: str, item_name: str) -\u003e None:\n        pass\n\n\nclass Controller(ABC):\n    \"\"\"Abstract controller defines interfaces.\"\"\"\n\n    @abstractmethod\n    def show_items(self):\n        pass\n\n    @abstractmethod\n    def show_item_information(self, item_name: str) -\u003e None:\n        pass\n\n\nclass ProductModel(Model):\n    \"\"\"Concrete product model.\"\"\"\n\n    class Price(float):\n        \"\"\"A polymorphic way to pass a float with a particular\n        __str__ functionality.\"\"\"\n\n        def __str__(self) -\u003e str:\n            first_digits_str: str = str(round(self, 2))\n            try:\n                dot_location: int = first_digits_str.index(\".\")\n            except ValueError:\n                return f\"{first_digits_str}.00\"\n            return f\"{first_digits_str}{'0' * (3 + dot_location - len(first_digits_str))}\"\n\n    products: Dict[str, Dict[str, Any]] = {\n        \"milk\": {\"price\": Price(1.50), \"quantity\": 10},\n        \"eggs\": {\"price\": Price(0.20), \"quantity\": 100},\n        \"cheese\": {\"price\": Price(2.00), \"quantity\": 10},\n    }\n\n    @property\n    def item_type(self) -\u003e str:\n        return \"product\"\n\n    def __iter__(self) -\u003e Iterator[str]:\n        for item in self.products:  # type: str\n            yield item\n\n    def get(self, item: str) -\u003e Dict[str, int]:\n        try:\n            return self.products[item]\n        except KeyError as error:\n            raise KeyError(\n                str(error) + \" not in the model's item list.\"\n            ) from error\n\n\nclass ConsoleView(View):\n    \"\"\"Concrete console view.\"\"\"\n\n    def show_item_list(self, item_type: str, item_list: Dict[str, Any]) -\u003e None:\n        print(\"{} LIST:\".format(item_type.upper()))\n        for item in item_list:\n            print(item)\n        print(\"\\n\")\n\n    @staticmethod\n    def capitalizer(string: str) -\u003e str:\n        return f\"{string[0].upper()}{ string[1:].lower()}\"\n\n    def show_item_information(\n        self, item_type: str, item_name: str, item_info: Dict[str, int]\n    ) -\u003e None:\n        print(f\"{item_type.upper()} INFORMATION:\")\n        printout: str = f\"Name: {item_name}\"\n        for key, value in item_info.items():\n            printout += \", \" + self.capitalizer(str(key)) + \": \" + str(value)\n        printout += \"\\n\"\n        print(printout)\n\n    def item_not_found(self, item_type: str, item_name: str) -\u003e None:\n        print(f'That \"{item_type}\" \"{item_name}\" does not exist in the records')\n\n\nclass ItemController(Controller):\n    \"\"\"Concrete item controller.\"\"\"\n\n    def __init__(self, item_model: Model, item_view: View) -\u003e None:\n        self._model = item_model\n        self._view = item_view\n\n    def show_items(self) -\u003e None:\n        items: List = list(self._model)\n        item_type: str = self._model.item_type\n        self._view.show_item_list(item_type, items)\n\n    def show_item_information(self, item_name: str) -\u003e None:\n        try:\n            item_info: Dict[str, Any] = self._model.get(item_name)\n        except KeyError:\n            item_type: str = self._model.item_type\n            self._view.item_not_found(item_type, item_name)\n        else:\n            item_type: str = self._model.item_type\n            self._view.show_item_information(item_type, item_name, item_info)\n\n\nif __name__ == \"__main__\":\n    model: Model = ProductModel()\n    view: View = ConsoleView()\n    controller: ItemController = ItemController(model, view)\n    controller.show_items()\n    controller.show_item_information(\"cheese\")\n    controller.show_item_information(\"eggs\")\n    controller.show_item_information(\"milk\")\n    controller.show_item_information(\"arepas\")\n\n\n# OUTPUT #\n# PRODUCT LIST:\n# cheese\n# eggs\n# milk\n#\n# PRODUCT INFORMATION:\n# Name: Cheese, Price: 2.00, Quantity: 10\n#\n# PRODUCT INFORMATION:\n# Name: Eggs, Price: 0.20, Quantity: 100\n#\n# PRODUCT INFORMATION:\n# Name: Milk, Price: 1.50, Quantity: 10\n#\n# That product \"arepas\" does not exist in the records\n```\n\n**[⬆ back to top](#table-of-contents)**\n\n### Decorator\nDecorator type of patterns add new feature to an existing object. Supports dynamic changes.\n- Exercise:\n  - Add additional message to an existing function\n\n**Decorator function**\n```python\nfrom functools import wraps\nfrom typing import Callable\n\n\ndef make_blink(function: Callable[[str], str]) -\u003e Callable[..., str]:\n    \"\"\"Defines the decorator function.\"\"\"\n\n    @wraps(function)\n    def decorator(*args, **kwargs) -\u003e str:\n        result: str = function(*args, **kwargs)\n        return f\"\u003cblink\u003e{result}\u003c/blink\u003e\"\n\n    return decorator\n\n\n@make_blink\ndef hello_world(name: str) -\u003e str:\n    \"\"\"Original function.\"\"\"\n    return f'Hello World said \"{name}\"!'\n\n\nprint(hello_world(name=\"James\"))\nprint(hello_world.__name__)\nprint(hello_world.__doc__)\n```\n\n**Decorator class**\n```python\nfrom abc import ABC, abstractmethod\n\n\nclass Number(ABC):\n    \"\"\"Abstraction of a number object.\"\"\"\n\n    @abstractmethod\n    def value(self) -\u003e int:\n        pass\n\n\nclass Integer(Number):\n    \"\"\"A subclass of a number.\"\"\"\n\n    def __init__(self, value: int) -\u003e None:\n        self._value = value\n\n    def value(self) -\u003e int:\n        return self._value\n\n\nclass Float(Number):\n    \"\"\"Decorator object converts `int` datatype into `float` datatype.\"\"\"\n\n    def __init__(self, number: Number) -\u003e None:\n        self._number: Number = number\n\n    def value(self) -\u003e float:\n        return float(self._number.value())\n\n\nclass SumOfFloat(Number):\n    \"\"\"Sum of two `float` numbers.\"\"\"\n\n    def __init__(self, one: Number, two: Number) -\u003e None:\n        self._one: Float = Float(one)\n        self._two: Float = Float(two)\n\n    def value(self) -\u003e float:\n        return self._one.value() + self._two.value()\n\n\ninteger_one: Number = Integer(value=5)\ninteger_two: Number = Integer(value=6)\nsum_float: Number = SumOfFloat(integer_one, integer_two)\nprint(sum_float.value())\n```\n\n**[⬆ back to top](#table-of-contents)**\n\n### Proxy\nProxy patterns postpones object creation unless it is necessary. Object is too expensive (resource intensive) to create that's why we have to create it once it is needed.\n- Participants:\n  - Producer\n  - Artist\n  - Guest\n- Clients interact with a Proxy. Proxy is responsible for creating the resource intensive objects\n\n```python\nimport time\n\n\nclass Producer:\n    \"\"\"Defines the resource-intensive object to instantiate.\"\"\"\n\n    def produce(self) -\u003e None:\n        print(\"Producer is working hard!\")\n\n    def meet(self) -\u003e None:\n        print(\"Producer has time to meet you now\")\n\n\nclass Proxy:\n    \"\"\"Defines the less resource-intensive object to instantiate as a middleman.\"\"\"\n\n    def __init__(self):\n        self._occupied: bool = False\n\n    @property\n    def occupied(self) -\u003e bool:\n        return self._occupied\n\n    @occupied.setter\n    def occupied(self, state: bool) -\u003e None:\n        if not isinstance(state, bool):\n            raise ValueError(f'\"{state}\" value should be a boolean data type!')\n        self._occupied = state\n\n    def produce(self) -\u003e None:\n        print(\"Artist checking if producer is available ...\")\n        if not self.occupied:\n            producer: Producer = Producer()\n            time.sleep(2)\n            producer.meet()\n        else:\n            time.sleep(2)\n            print(\"Producer is busy!\")\n\n\nproxy: Proxy = Proxy()\nproxy.produce()\nproxy.occupied = True\nproxy.produce()\n```\n\n**[⬆ back to top](#table-of-contents)**\n\n### Adapter\nAdapter patterns convert interface of a class into another one that client is expecting.\n- Exercise:\n  - Korean language: `speak_korean()`\n  - British language: `speak_english()`\n  - Client has to have uniform interface - `speak method`\n- Solution:\n  - Use an adapter pattern that translates method name between client and the server code\n\n```python\nfrom abc import ABC, abstractmethod\nfrom typing import Any\n\n\nclass Speaker(ABC):\n    \"\"\"Abstract interface for some speaker.\"\"\"\n\n    @abstractmethod\n    def type(self) -\u003e str:\n        pass\n\n\nclass Korean(Speaker):\n    \"\"\"Korean speaker.\"\"\"\n\n    def __init__(self) -\u003e None:\n        self._type: str = \"Korean\"\n\n    def type(self) -\u003e str:\n        return self._type\n\n    def speak_korean(self) -\u003e str:\n        return \"An-neyong?\"\n\n\nclass British(Speaker):\n    \"\"\"English speaker.\"\"\"\n\n    def __init__(self):\n        self._type: str = \"British\"\n\n    def type(self) -\u003e str:\n        return self._type\n\n    def speak_english(self) -\u003e str:\n        return \"Hello\"\n\n\nclass Adapter:\n    \"\"\"Changes the generic method name to individualized method names.\"\"\"\n\n    def __init__(self, obj: Any, **adapted_method: Any) -\u003e None:\n        self._object = obj\n        self.__dict__.update(adapted_method)\n\n    def __getattr__(self, item: Any) -\u003e Any:\n        return getattr(self._object, item)\n\n\nspeakers: list = []\nkorean = Korean()\nbritish = British()\nspeakers.append(Adapter(korean, speak=korean.speak_korean))\nspeakers.append(Adapter(british, speak=british.speak_english))\n\nfor speaker in speakers:\n    print(f\"{speaker.type()} says '{speaker.speak()}'\")\n```\n\n**[⬆ back to top](#table-of-contents)**\n\n### Composite\n- Exercise:\n  - Build recursive tree data structure. (Menu \u003e submenu \u003e sub-submenu \u003e ...)\n- Participants:\n  - Component - abstract 'class'\n  - Child - inherits from Component 'class'\n  - Composite - inherits from component 'class'. Maintain child objects by adding.removing them\n\n```python\nfrom abc import ABC, abstractmethod\nfrom typing import Sequence, List\n\n\nclass Component(ABC):\n    \"\"\"Abstract interface of some component.\"\"\"\n\n    @abstractmethod\n    def function(self) -\u003e None:\n        pass\n\n\nclass Child(Component):\n    \"\"\"Concrete child component.\"\"\"\n\n    def __init__(self, *args: str) -\u003e None:\n        self._args: Sequence[str] = args\n\n    def name(self) -\u003e str:\n        return self._args[0]\n\n    def function(self) -\u003e None:\n        print(f'\"{self.name()}\" component')\n\n\nclass Composite(Component):\n    \"\"\"Concrete class maintains the tree recursive structure.\"\"\"\n\n    def __init__(self, *args: str) -\u003e None:\n        self._args: Sequence[str] = args\n        self._children: List[Component] = []\n\n    def name(self) -\u003e str:\n        return self._args[0]\n\n    def append_child(self, child: Component) -\u003e None:\n        self._children.append(child)\n\n    def remove_child(self, child: Component) -\u003e None:\n        self._children.remove(child)\n\n    def function(self) -\u003e None:\n        print(f'\"{self.name()}\" component')\n        for child in self._children:  # type: Component\n            child.function()\n\n\ntop_menu = Composite(\"top_menu\")\n\nsubmenu_one = Composite(\"submenu one\")\nchild_submenu_one = Child(\"sub_submenu one\")\nchild_submenu_two = Child(\"sub_submenu two\")\nsubmenu_one.append_child(child_submenu_one)\nsubmenu_one.append_child(child_submenu_two)\n\nsubmenu_two = Child(\"submenu two\")\ntop_menu.append_child(submenu_one)\ntop_menu.append_child(submenu_two)\ntop_menu.function()\n```\n\n**[⬆ back to top](#table-of-contents)**\n\n### Bridge\nBridge pattern separates the abstraction into different class hierarchies. \nAbstract factory and adapter patterns are related to Bridge design pattern.\n\n```python\nfrom abc import ABC, abstractmethod\n\n\nclass DrawApi(ABC):\n    \"\"\"Provides draw interface.\"\"\"\n\n    @abstractmethod\n    def draw_circle(self, x: int, y: int, radius: int) -\u003e None:\n        pass\n\n\nclass Circle(ABC):\n    \"\"\"Provides circle shape interface.\"\"\"\n\n    @abstractmethod\n    def draw(self) -\u003e None:\n        pass\n\n    @abstractmethod\n    def scale(self, percentage: int) -\u003e None:\n        pass\n\n\nclass DrawApiOne(DrawApi):\n    \"\"\"Implementation-specific abstraction: concrete class one.\"\"\"\n\n    def draw_circle(self, x: int, y: int, radius: int) -\u003e None:\n        print(f\"API 1 drawing a circle at ({x}, {y} with radius {radius}!)\")\n\n\nclass DrawApiTwo(DrawApi):\n    \"\"\"Implementation-specific abstraction: concrete class two.\"\"\"\n\n    def draw_circle(self, x: int, y: int, radius: int) -\u003e None:\n        print(f\"API 2 drawing a circle at ({x}, {y} with radius {radius}!)\")\n\n\nclass DrawCircle(Circle):\n    \"\"\"Implementation-independent abstraction: e.g there could be a rectangle class!.\"\"\"\n\n    def __init__(self, x: int, y: int, radius: int, draw_api: DrawApi) -\u003e None:\n        self._x: int = x\n        self._y: int = y\n        self._radius: int = radius\n        self._draw_api: DrawApi = draw_api\n\n    def draw(self) -\u003e None:\n        self._draw_api.draw_circle(self._x, self._y, self._radius)\n\n    def scale(self, percentage: int) -\u003e None:\n        if not isinstance(percentage, int):\n            raise ValueError(\n                f'\"{percentage}\" value should be an integer data type!'\n            )\n        self._radius *= percentage\n\n\ncircle_one: Circle = DrawCircle(1, 2, 3, DrawApiOne())\ncircle_one.draw()\ncircle_two: Circle = DrawCircle(3, 4, 6, DrawApiTwo())\ncircle_two.draw()\n```\n\n**[⬆ back to top](#table-of-contents)**\n\n### Facade\nThe Facade pattern is a way to provide a simpler unified interface to a more complex system. \nIt provides an easier way to access functions of the underlying system by providing a single entry point.\n\n```python\nfrom abc import ABC, abstractmethod\nimport time\nfrom typing import List, Tuple, Iterator, Type\n\n_sleep: float = 0.2\n\n\nclass TestCase(ABC):\n    \"\"\"Abstract test case interface.\"\"\"\n\n    @abstractmethod\n    def run(self) -\u003e None:\n        pass\n\n\nclass TestCaseOne(TestCase):\n    \"\"\"Concrete test case one.\"\"\"\n\n    def __init__(self, name: str) -\u003e None:\n        self._name: str = name\n\n    def run(self) -\u003e None:\n        print(\"{:#^20}\".format(self._name))\n        time.sleep(_sleep)\n        print(\"Setting up testcase one\")\n        time.sleep(_sleep)\n        print(\"Running test\")\n        time.sleep(_sleep)\n        print(\"Tearing down\")\n        time.sleep(_sleep)\n        print(\"Test Finished\\n\")\n\n\nclass TestCaseTwo(TestCase):\n    \"\"\"Concrete test case two.\"\"\"\n\n    def __init__(self, name: str) -\u003e None:\n        self._name: str = name\n\n    def run(self) -\u003e None:\n        print(\"{:#^20}\".format(self._name))\n        time.sleep(_sleep)\n        print(\"Setting up testcase two\")\n        time.sleep(_sleep)\n        print(\"Running test\")\n        time.sleep(_sleep)\n        print(\"Tearing down\")\n        time.sleep(_sleep)\n        print(\"Test Finished\\n\")\n\n\nclass TestCaseThree(TestCase):\n    \"\"\"Concrete test case three.\"\"\"\n\n    def __init__(self, name: str) -\u003e None:\n        self._name: str = name\n\n    def run(self) -\u003e None:\n        print(\"{:#^20}\".format(self._name))\n        time.sleep(_sleep)\n        print(\"Setting up testcase three\")\n        time.sleep(_sleep)\n        print(\"Running test\")\n        time.sleep(_sleep)\n        print(\"Tearing down\")\n        time.sleep(_sleep)\n        print(\"Test Finished\\n\")\n\n\nclass TestSuite:\n    \"\"\"Represents simpler unified interface to run all test cases.\n\n    A facade class itself.\n    \"\"\"\n\n    def __init__(self, testcases: List[TestCase]) -\u003e None:\n        self._testcases = testcases\n\n    def run(self) -\u003e None:\n        for testcase in self._testcases:  # type: TestCase\n            testcase.run()\n\n\ntest_cases: List[TestCase] = [\n    TestCaseOne(\"TC1\"),\n    TestCaseTwo(\"TC2\"),\n    TestCaseThree(\"TC3\")\n]\ntest_suite = TestSuite(test_cases)\ntest_suite.run()\n\n\nclass Interface(ABC):\n    \"\"\"Abstract interface.\"\"\"\n\n    @abstractmethod\n    def run(self) -\u003e str:\n        pass\n\n\nclass A(Interface):\n    \"\"\"Implement interface.\"\"\"\n\n    def run(self) -\u003e str:\n        return \"A.run()\"\n\n\nclass B(Interface):\n    \"\"\"Implement interface.\"\"\"\n\n    def run(self) -\u003e str:\n        return \"B.run()\"\n\n\nclass C(Interface):\n    \"\"\"Implement interface.\"\"\"\n\n    def run(self) -\u003e str:\n        return \"C.run()\"\n\n\nclass Facade(Interface):\n    \"\"\"Facade object.\"\"\"\n\n    def __init__(self):\n        self._all: Tuple[Type[Interface], ...] = (A, B, C)\n\n    def run(self) -\u003e Iterator[Interface]:\n        yield from self._all\n\n\nif __name__ == \"__main__\":\n    print(*(cls().run() for cls in Facade().run()))\n```\n\n**[⬆ back to top](#table-of-contents)**\n\n## Behavioral\nBehavioral patterns provide best practices of objects interaction. Methods and signatures are often used.\n\n### Observer\nObserver pattern establishes one to many relationship between subject and multiple observers. Singleton is related to observer design pattern.\n- Exercise:\n  - Subjects need to be monitored\n  - Observers need to be notified\n- Participants:\n  - Subject: abstract class\n    - Attach\n    - Detach\n    - Notify\n  - Concrete Subjects\n\n```python\nfrom typing import List\n\n\nclass Subject:\n    \"\"\"Represents what is being observed. Needs to be monitored.\"\"\"\n\n    def __init__(self, name: str = \"\") -\u003e None:\n        self._observers: List[\"TempObserver\"] = []\n        self._name: str = name\n        self._temperature: int = 0\n\n    def attach(self, observer: \"TempObserver\") -\u003e None:\n        if observer not in self._observers:\n            self._observers.append(observer)\n\n    def detach(self, observer: \"TempObserver\") -\u003e None:\n        try:\n            self._observers.remove(observer)\n        except ValueError:\n            pass\n\n    def notify(self, modifier=None) -\u003e None:\n        for observer in self._observers:\n            if modifier != observer:\n                observer.update(self)\n\n    @property\n    def name(self) -\u003e str:\n        return self._name\n\n    @property\n    def temperature(self) -\u003e int:\n        return self._temperature\n\n    @temperature.setter\n    def temperature(self, temperature: int) -\u003e None:\n        if not isinstance(temperature, int):\n            raise ValueError(f'\"{temperature}\" value should be an integer data type!')\n        self._temperature = temperature\n\n\nclass TempObserver:\n    \"\"\"Represents an observer class. Needs to be notified.\"\"\"\n\n    def update(self, subject: Subject) -\u003e None:\n        print(f\"Temperature Viewer: {subject.name} has Temperature {subject.temperature}\")\n\n\nsubject_one = Subject(\"Subject One\")\nsubject_two = Subject(\"Subject Two\")\n\nobserver_one = TempObserver()\nobserver_two = TempObserver()\n\nsubject_one.attach(observer_one)\nsubject_one.attach(observer_two)\n\nsubject_one.temperature = 80\nsubject_one.notify()\n\nsubject_one.temperature = 90\nsubject_one.notify()\n```\n\n**[⬆ back to top](#table-of-contents)**\n\n### Visitor\nVisitor pattern adds new features to existing hierarchy without changing it. Add new operations to existing classes dynamically.\nExercise:\n  - House class:\n    - HVAC specialist: Visitor type 1\n    - Electrician: Visitor type 2\n\n```python\nfrom abc import ABC, abstractmethod\n\n\nclass Visitor(ABC):\n    \"\"\"Abstract visitor.\"\"\"\n\n    @abstractmethod\n    def visit(self, house: \"House\") -\u003e None:\n        pass\n\n    def __str__(self) -\u003e str:\n        return self.__class__.__name__\n\n\nclass House(ABC):\n    \"\"\"Abstract house.\"\"\"\n\n    @abstractmethod\n    def accept(self, visitor: Visitor) -\u003e None:\n        pass\n\n    @abstractmethod\n    def work_on_hvac(self, specialist: Visitor) -\u003e None:\n        pass\n\n    @abstractmethod\n    def work_on_electricity(self, specialist: Visitor) -\u003e None:\n        pass\n\n    def __str__(self) -\u003e str:\n        return self.__class__.__name__\n\n\nclass ConcreteHouse(House):\n    \"\"\"Represent concrete house.\"\"\"\n\n    def accept(self, visitor: Visitor) -\u003e None:\n        visitor.visit(self)\n\n    def work_on_hvac(self, specialist: Visitor) -\u003e None:\n        print(self, \"worked on by\", specialist)\n\n    def work_on_electricity(self, specialist: Visitor) -\u003e None:\n        print(self, \"worked on by\", specialist)\n\n\nclass HvacSpecialist(Visitor):\n    \"\"\"Concrete visitor: HVAC specialist.\"\"\"\n\n    def visit(self, house: House) -\u003e None:\n        house.work_on_hvac(self)\n\n\nclass Electrician(Visitor):\n    \"\"\"Concrete visitor: electrician.\"\"\"\n\n    def visit(self, house: House) -\u003e None:\n        house.work_on_electricity(self)\n\n\nhvac: Visitor = HvacSpecialist()\nelectrician: Visitor = Electrician()\nhome: House = ConcreteHouse()\nhome.accept(hvac)\nhome.accept(electrician)\n```\n\n**[⬆ back to top](#table-of-contents)**\n\n### Iterator\nComposite pattern is related to iterator pattern.\n- Exercise:\n  - Our custom iterator based on a build-in python iterator: `zip()`\n  - Will iterate over a certain point based on client input\n\n**Iterator function**\n\n```python\nfrom typing import Iterator, Tuple, List\n\n\ndef count_to(count: int) -\u003e Iterator[Tuple[int, str]]:\n    \"\"\"Our iterator implementation.\"\"\"\n    numbers_in_german: List[str] = [\"einn\", \"zwei\", \"drei\", \"veir\", \"funf\"]\n    iterator: Iterator[Tuple[int, str]] = zip(range(1, count + 1), numbers_in_german)\n    for position, number in iterator:  # type: int, str\n        yield position, number\n\n\nfor number_ in count_to(3):  # type: Tuple[int]\n    print(\"{} in german is {}\".format(*number_))\n\n\nclass IteratorSequence:\n    \"\"\"Represent iterator sequence object.\"\"\"\n\n    def __init__(self, capacity: int) -\u003e None:\n        self._range: Iterator[int] = iter(range(capacity))\n\n    def __next__(self) -\u003e int:\n        return next(self._range)\n\n    def __iter__(self) -\u003e Iterator[int]:\n        return self\n\n\niterator_: IteratorSequence = IteratorSequence(capacity=10)\nfor _ in range(10):  # type: int\n    print(next(iterator_))\n```\n\n**[⬆ back to top](#table-of-contents)**\n\n### Strategy\nStrategy patterns used to dynamically change the behavior of an object. Add dynamically objects with `types` module.\n- Participants:\n  - Abstract strategy class with default set of behaviors\n  - Concrete strategy class with new behaviors\n\n```python\nimport types\nfrom typing import Callable, Any\n\n\nclass Strategy:\n    \"\"\"A strategy pattern class.\"\"\"\n\n    def __init__(self, func: Callable[[\"Strategy\"], Any] = None) -\u003e None:\n        self._name: str = \"Default strategy\"\n        if func:\n            self.execute = types.MethodType(func, self)\n\n    @property\n    def name(self) -\u003e str:\n        return self._name\n\n    @name.setter\n    def name(self, name: str) -\u003e None:\n        if not isinstance(name, str):\n            raise ValueError(f'\"{name}\" value should be a string data type!')\n        self._name = name\n\n    def execute(self):\n        print(f\"{self._name} is used\")\n\n\ndef strategy_function_one(strategy: Strategy) -\u003e None:\n    print(f\"{strategy.name} is used to execute method one\")\n\n\ndef strategy_function_two(strategy: Strategy) -\u003e None:\n    print(f\"{strategy.name} is used to execute method two\")\n\n\ndefault_strategy = Strategy()\ndefault_strategy.execute()\n\nfirst_strategy = Strategy(func=strategy_function_one)\nfirst_strategy.name = \"Strategy one\"\nfirst_strategy.execute()\n\nsecond_strategy = Strategy(func=strategy_function_two)\nsecond_strategy.name = \"Strategy two\"\nsecond_strategy.execute()\n```\n\n**[⬆ back to top](#table-of-contents)**\n\n### Chain of responsibility\nThis type of pattern decouples responsibility. Composite is related to this design pattern.\n- Exercise:\n  - Integer value\n  - Handlers\n    - Find out its range\n- Participants:\n  - Abstract handler\n    - Successor\n  - Concrete Handler\n    - Checks if it can handle the request\n\n```python\nfrom abc import abstractmethod\nfrom typing import List\n\n\nclass Handler:\n    \"\"\"Abstract handler.\"\"\"\n\n    def __init__(self, successor: \"Handler\") -\u003e None:\n        self._successor: Handler = successor\n\n    def handler(self, request: int) -\u003e None:\n        if not self.handle(request):\n            self._successor.handler(request)\n\n    @abstractmethod\n    def handle(self, request: int) -\u003e bool:\n        pass\n\n\nclass ConcreteHandler1(Handler):\n    \"\"\"Concrete handler 1.\"\"\"\n\n    def handle(self, request: int) -\u003e bool:\n        if 0 \u003c request \u003c= 10:\n            print(f\"Request {request} handled in handler 1\")\n            return True\n        return False\n\n\nclass DefaultHandler(Handler):\n    \"\"\"Default handler.\"\"\"\n\n    def handle(self, request: int) -\u003e bool:\n        \"\"\"If there is no handler available.\"\"\"\n        print(f\"End of chain, no handler for {request}\")\n        return True\n\n\nclass Client:\n    \"\"\"Using handlers.\"\"\"\n\n    def __init__(self) -\u003e None:\n        self._handler: Handler = ConcreteHandler1(DefaultHandler(None))\n\n    def delegate(self, request: List[int]) -\u003e None:\n        for next_request in request:\n            self._handler.handler(next_request)\n\n\n# Create a client\nclient: Client = Client()\n\n# Create requests\nrequests: List[int] = [2, 5, 30]\n\n# Send the request\nclient.delegate(requests)\n```\n\n**[⬆ back to top](#table-of-contents)**\n\n## Development notes\n\n### Code analysis\nFrom the root directory of your shell please run following command to start static code assessment (it will check code with linter rules and unit testing):\n\n```bash\n./run-code-analysis.sh \n```\n\n### Commit template\n\nPlease use the following command to include gitcommit message template within the project:\n```bash\ngit config commit.template .gitcommit.txt\n```\n\n### Release notes\n\nPlease check [changelog](CHANGELOG.md) file to get more details about actual versions and it's release notes.\n\n### Meta\nAuthor – Volodymyr Yahello vyahello@gmail.com\n\nDistributed under the `MIT` license. See [license](LICENSE.md) for more information.\n\nYou can reach out me at:\n* [vyahello@gmail.com](vyahello@gmail.com)\n* [https://twitter.com/vyahello](https://twitter.com/vyahello)\n* [https://www.linkedin.com/in/volodymyr-yahello-821746127](https://www.linkedin.com/in/volodymyr-yahello-821746127)\n\n### Contributing\nI would highly appreciate any contribution and support. If you are interested to add your ideas into project please follow next simple steps:\n\n1. Clone the repository\n2. Configure `git` for the first time after cloning with your `name` and `email`\n3. `pip install -r requirements.txt` to install all project dependencies\n4. Create your feature branch (git checkout -b feature/fooBar)\n5. Commit your changes (git commit -am 'Add some fooBar')\n6. Push to the branch (git push origin feature/fooBar)\n7. Create a new Pull Request\n\n### What's next\n\nAll recent activities and ideas are described at project [issues](https://github.com/vyahello/python-ood/issues) page. \nIf you have ideas you want to change/implement please do not hesitate and create an issue.\n\n**[⬆ back to top](#table-of-contents)**\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvyahello%2Fpython-ood","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvyahello%2Fpython-ood","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvyahello%2Fpython-ood/lists"}