├── .gitignore ├── LICENSE ├── README.md ├── behavioral ├── chain_of_responsibility.py ├── classic_iterator.py ├── command.py ├── mediator.py ├── memento.py ├── observer.py ├── python_style_iterator.py ├── state.py ├── strategy.py ├── template_method.py └── visitor.py ├── creational ├── abstract_factory.py ├── borg.py ├── builder_with_director.py ├── builder_without_director.py ├── classic_singleton.py ├── factory_method.py ├── first_lazy_initialization.py ├── method_chaining.py ├── method_chaining_with_builder.py ├── multithread_singleton.py ├── object_pool.py ├── prototype.py └── second_lazy_initialization.py └── structural ├── adapter.py ├── bridge.py ├── composite.py ├── decorator.py ├── facade.py ├── flyweight.py └── proxy.py /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__/ 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Stanislav Chernyshev 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # design-patterns 2 | Code examples from my book "Principles and software design patterns": https://urait.ru/bcode/497029 3 | -------------------------------------------------------------------------------- /behavioral/chain_of_responsibility.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | from enum import Enum 3 | from typing import List, Optional, TypeVar 4 | 5 | T = TypeVar("T") 6 | 7 | 8 | class EnumOrder(Enum): 9 | """Тип Заказа""" 10 | VEGAN = 1 11 | NOT_VEGAN = 2 12 | BINGE = 3 13 | NOT_ORDER = 4 14 | 15 | 16 | class RequestOrder: 17 | """Класс запроса на обслуживание от клиента""" 18 | 19 | def __init__(self, description: List[str], order_type: EnumOrder): 20 | self.__description = description 21 | self.__order_type = order_type 22 | 23 | @property 24 | def order_type(self): 25 | return self.__order_type 26 | 27 | @property 28 | def order_list(self): 29 | return self.__description 30 | 31 | 32 | class Handler(ABC): 33 | """Базовый класс для обработчиков запросов""" 34 | 35 | def __init__(self, successor: Optional[T] = None): 36 | self.__successor = successor 37 | 38 | def handle(self, request: RequestOrder) -> None: 39 | res = self._check_request(request.order_type) 40 | if not res and self.__successor: 41 | self.__successor.handle(request) 42 | 43 | @property 44 | def successor(self): 45 | return self.__successor 46 | 47 | @successor.setter 48 | def successor(self, successor: Optional[T]): 49 | self.__successor = successor 50 | 51 | @abstractmethod 52 | def _check_request(self, request_type: EnumOrder) -> bool: 53 | ... 54 | 55 | 56 | class WaiterHandler(Handler): 57 | """Класс обработки запроса официантом""" 58 | 59 | def __init__(self, successor: Handler = None): 60 | super().__init__(successor) 61 | 62 | def _check_request(self, request_type: EnumOrder) -> bool: 63 | check = request_type in (EnumOrder.BINGE, 64 | EnumOrder.VEGAN, 65 | EnumOrder.NOT_VEGAN) 66 | if check: 67 | print("Официант передает заказ дальше") 68 | else: 69 | print("Официант отклоняет запрос клиента") 70 | return not check 71 | 72 | 73 | class KitchenHandler(Handler): 74 | """Класс обработки запроса кухней пицерии""" 75 | 76 | def __init__(self, successor: Handler = None): 77 | super().__init__(successor) 78 | 79 | def _check_request(self, request_type: EnumOrder) -> bool: 80 | check = request_type in (EnumOrder.VEGAN, 81 | EnumOrder.NOT_VEGAN) 82 | if check: 83 | print("Шеф-повар приступил к готовке заказа на кухне") 84 | else: 85 | print("Шеф-повар воротит нос от поступившего запроса") 86 | return check 87 | 88 | 89 | class BarmanHandler(Handler): 90 | """Класс обработки запроса на стойке бара""" 91 | 92 | def __init__(self, successor: Handler = None): 93 | super().__init__(successor) 94 | 95 | def _check_request(self, request_type: EnumOrder) -> bool: 96 | check = request_type is EnumOrder.BINGE 97 | if check: 98 | print("Бармен наливает заказанный напиток клиентом") 99 | else: 100 | print("Бармен разводит руками и может только восхититься " 101 | "гурманским вкусам клиента") 102 | return check 103 | 104 | 105 | if __name__ == "__main__": 106 | # Настраиваем цепочку обработки запросов 107 | kitchen = KitchenHandler() 108 | bar = BarmanHandler(kitchen) 109 | waiter = WaiterHandler() 110 | # запрос всегда первым поступает официанту 111 | waiter.successor = bar 112 | def request_handler(request: RequestOrder): 113 | print("*"*10+"Обработка запроса"+"*"*10) 114 | print(f"Запрос от клиента: {request.order_list}") 115 | waiter.handle(request) 116 | 117 | req_list = ['Борщ', 'Макарошки по-флотски'] 118 | request = RequestOrder(req_list, EnumOrder.NOT_VEGAN) 119 | request_handler(request) 120 | 121 | req_list = ['Кровавая Мерри', 'Коньяк', 'Виски'] 122 | request = RequestOrder(req_list, EnumOrder.BINGE) 123 | request_handler(request) 124 | 125 | req_list = ['Мир на блюдечке!'] 126 | request = RequestOrder(req_list, EnumOrder.NOT_ORDER) 127 | request_handler(request) 128 | -------------------------------------------------------------------------------- /behavioral/classic_iterator.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | from typing import List 3 | 4 | 5 | class PizzaItem: 6 | def __init__(self, number): 7 | self.number = number 8 | 9 | def __str__(self): 10 | return f"кусочек пиццы под номером: {self.number}" 11 | 12 | 13 | class Iterator(ABC): 14 | @abstractmethod 15 | def next(self) -> PizzaItem: 16 | ... 17 | 18 | @abstractmethod 19 | def has_next(self) -> bool: 20 | ... 21 | 22 | 23 | class PizzaSliceIterator(Iterator): 24 | def __init__(self, pizza: List[PizzaItem]): 25 | self._pizza = pizza 26 | self._index = 0 27 | 28 | def next(self) -> PizzaItem: 29 | pizza_item = self._pizza[self._index] 30 | self._index += 1 31 | return pizza_item 32 | 33 | def has_next(self) -> bool: 34 | return False if self._index >= len(self._pizza) else True 35 | 36 | 37 | class PizzaAggregate: 38 | def __init__(self, amount_slices: int = 10): 39 | self.slices = [PizzaItem(it+1) for it in range(amount_slices)] 40 | print(f"Приготовили пиццу и порезали " 41 | f"на {amount_slices} кусочков") 42 | 43 | def amount_slices(self) -> int: 44 | return len(self.slices) 45 | 46 | def iterator(self) -> Iterator: 47 | return PizzaSliceIterator(self.slices) 48 | 49 | 50 | if __name__ == "__main__": 51 | pizza = PizzaAggregate(5) 52 | iterator = pizza.iterator() 53 | while iterator.has_next(): 54 | item = iterator.next() 55 | print("Это " + str(item)) 56 | print("*"*20) 57 | iterator = pizza.iterator() 58 | iterator.next() 59 | while iterator.has_next(): 60 | item = iterator.next() 61 | print("Это " + str(item)) 62 | -------------------------------------------------------------------------------- /behavioral/command.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | from typing import List 3 | 4 | 5 | class ICommand(ABC): 6 | """Интерфейсный класс для выполняемых операций""" 7 | @abstractmethod 8 | def execute(self) -> None: 9 | ... 10 | 11 | 12 | class ChiefAssistant: 13 | def prepare_pizza_dough(self): 14 | print("Ассистент подготавливает тесто для пиццы") 15 | 16 | def prepare_topping(self): 17 | print("Ассистент нарезает начинку для пиццы") 18 | 19 | def prepare_sauce(self): 20 | print("Ассистент готовит соус") 21 | 22 | 23 | class Stove: 24 | def prepare_stove(self): 25 | print("Печь разогревается") 26 | 27 | def cooking_pizza(self): 28 | print("Пицца готовится в печи") 29 | 30 | 31 | class ChiefCooker: 32 | def make_pizza_base(self): 33 | print("Шеф раскатывает основу для пиццы") 34 | 35 | def applied_sauce(self): 36 | print("Шеф наносит соус на основу пиццы") 37 | 38 | def add_topping_to_pizza(self): 39 | print("Шеф добавляет начинку на пиццу") 40 | 41 | def bon_appetit(self): 42 | print("Шеф желает клиенту приятного аппетита!") 43 | 44 | 45 | class PrepareStoveCommand(ICommand): 46 | """Класс команды для разогрева печи""" 47 | def __init__(self, executor: Stove): 48 | self.__executor = executor 49 | 50 | def execute(self) -> None: 51 | self.__executor.prepare_stove() 52 | 53 | 54 | class PrepareDoughCommand(ICommand): 55 | """Класс команды для подготовки теста пиццы""" 56 | def __init__(self, executor: ChiefAssistant): 57 | self.__executor = executor 58 | 59 | def execute(self) -> None: 60 | self.__executor.prepare_pizza_dough() 61 | 62 | 63 | class PrepareToppingCommand(ICommand): 64 | """Класс команды для нарезки начинки пиццы""" 65 | def __init__(self, executor: ChiefAssistant): 66 | self.__executor = executor 67 | 68 | def execute(self) -> None: 69 | self.__executor.prepare_topping() 70 | 71 | 72 | class PrepareSauceCommand(ICommand): 73 | """Класс команды для приготовления соуса""" 74 | def __init__(self, executor: ChiefAssistant): 75 | self.__executor = executor 76 | 77 | def execute(self) -> None: 78 | self.__executor.prepare_sauce() 79 | 80 | 81 | class CookingPizzaCommand(ICommand): 82 | """Класс команды для приготовления пиццы в печи""" 83 | def __init__(self, executor: Stove): 84 | self.__executor = executor 85 | 86 | def execute(self) -> None: 87 | self.__executor.cooking_pizza() 88 | 89 | 90 | class MakePizzaBaseCommand(ICommand): 91 | """Класс команды для приготовления основы для пиццы""" 92 | def __init__(self, executor: ChiefCooker): 93 | self.__executor = executor 94 | 95 | def execute(self) -> None: 96 | self.__executor.make_pizza_base() 97 | 98 | 99 | class AppliedSauceCommand(ICommand): 100 | """Класс команды для нанесения соуса на пиццу""" 101 | def __init__(self, executor: ChiefCooker): 102 | self.__executor = executor 103 | 104 | def execute(self) -> None: 105 | self.__executor.applied_sauce() 106 | 107 | 108 | class AddToppingCommand(ICommand): 109 | """Класс команды для добавления начинки на пиццу""" 110 | 111 | def __init__(self, executor: ChiefCooker): 112 | self.__executor = executor 113 | 114 | def execute(self) -> None: 115 | self.__executor.add_topping_to_pizza() 116 | 117 | 118 | class BonAppetitCommand(ICommand): 119 | """Класс команды для пожелания клиенту 120 | приятного аппетита""" 121 | 122 | def __init__(self, executor: ChiefCooker): 123 | self.__executor = executor 124 | 125 | def execute(self) -> None: 126 | self.__executor.bon_appetit() 127 | 128 | 129 | class Pizzeria: 130 | """Класс агрегации всех команд для приготовления 131 | пиццы""" 132 | def __init__(self): 133 | self.history: List[ICommand] = [] 134 | 135 | def addCommand(self, command: ICommand) -> None: 136 | self.history.append(command) 137 | 138 | def cook(self) -> None: 139 | if not self.history: 140 | print("Не задана очередность выполнения" 141 | " команд приготовления пиццы") 142 | else: 143 | for executor in self.history: 144 | executor.execute() 145 | self.history.clear() 146 | 147 | 148 | if __name__ == "__main__": 149 | chief = ChiefCooker() 150 | assistant = ChiefAssistant() 151 | stove = Stove() 152 | pizzeria = Pizzeria() 153 | # формируем последовательность команд для приготовления пиццы 154 | pizzeria.addCommand(PrepareDoughCommand(assistant)) 155 | pizzeria.addCommand(MakePizzaBaseCommand(chief)) 156 | pizzeria.addCommand(PrepareSauceCommand(assistant)) 157 | pizzeria.addCommand(AppliedSauceCommand(chief)) 158 | pizzeria.addCommand(PrepareStoveCommand(stove)) 159 | pizzeria.addCommand(PrepareToppingCommand(assistant)) 160 | pizzeria.addCommand(AddToppingCommand(chief)) 161 | pizzeria.addCommand(CookingPizzaCommand(stove)) 162 | pizzeria.addCommand(BonAppetitCommand(chief)) 163 | # запускаем процесс приготовления пиццы 164 | pizzeria.cook() 165 | -------------------------------------------------------------------------------- /behavioral/mediator.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | from abc import ABC, abstractmethod 3 | from enum import Enum 4 | from typing import List 5 | from random import choice 6 | 7 | 8 | class OrderType(Enum): 9 | """Типы возможных заказов""" 10 | FOOD = 1 11 | BINGE = 2 12 | 13 | 14 | class Order: 15 | """Класс заказа""" 16 | order_id: int = 1 17 | 18 | def __init__(self, order_type: OrderType): 19 | self.id = Order.order_id 20 | self.type = order_type 21 | Order.order_id += 1 22 | 23 | def __str__(self): 24 | return f"заказ под #{self.id} ({self.type.name})" 25 | 26 | 27 | class Event(Enum): 28 | """Типы событий обработки заказов""" 29 | GET_ORDER = 1 30 | FINISH_ORDER = 2 31 | 32 | 33 | class WorkerType(Enum): 34 | """Типы работников пиццерии""" 35 | WAITER = 1 36 | CHIEF = 2 37 | BARMAN = 3 38 | 39 | 40 | class IMediator(ABC): 41 | """Интерфейс посредника""" 42 | 43 | @abstractmethod 44 | def notify(self, worker: Worker, order: Order, event: Event): 45 | ... 46 | 47 | @abstractmethod 48 | def add_worker(self, worker: Worker) -> None: 49 | ... 50 | 51 | 52 | class Worker(ABC): 53 | """Абстрактный базовый класс для 54 | всех сотрудников пиццерии""" 55 | 56 | def __init__(self, name: str, mediator: IMediator): 57 | self.mediator = mediator 58 | self.name = name 59 | self.orders = [] 60 | mediator.add_worker(self) 61 | 62 | @abstractmethod 63 | def take_order(self, order: Order): 64 | ... 65 | 66 | @abstractmethod 67 | def finish_order(self, order: Order): 68 | ... 69 | 70 | @abstractmethod 71 | def type(self) -> WorkerType: 72 | ... 73 | 74 | def get_orders_id(self) -> List[int]: 75 | return [it.id for it in self.orders] 76 | 77 | 78 | class Waiter(Worker): 79 | """Класс официанта""" 80 | 81 | def __init__(self, name: str, mediator: IMediator): 82 | super().__init__(name, mediator) 83 | 84 | def take_order(self, order: Order): 85 | self.orders.append(order) 86 | print(f"Официант {self.name} принял {order}") 87 | self.mediator.notify(self, order, Event.GET_ORDER) 88 | 89 | def finish_order(self, order: Order): 90 | print(f"Официант {self.name} отнес {order} клиенту") 91 | self.orders.remove(order) 92 | 93 | def type(self) -> WorkerType: 94 | return WorkerType.WAITER 95 | 96 | 97 | class Barman(Worker): 98 | """Класс бармена""" 99 | 100 | def __init__(self, name: str, mediator: IMediator): 101 | super().__init__(name, mediator) 102 | 103 | def take_order(self, order: Order): 104 | self.orders.append(order) 105 | print(f"Бармен {self.name} принял {order}") 106 | 107 | def finish_order(self, order: Order): 108 | print(f"Бармен {self.name} выполнил ") 109 | self.mediator.notify(self, order, Event.FINISH_ORDER) 110 | 111 | def processing_order(self): 112 | if self.orders: 113 | order = self.orders.pop() 114 | print(f"Бармен {self.name} выполняет {order}") 115 | self.finish_order(order) 116 | else: 117 | print(f"Бармен {self.name} грустно разводит руками") 118 | 119 | def type(self) -> WorkerType: 120 | return WorkerType.BARMAN 121 | 122 | 123 | class Chief(Worker): 124 | """Класс шеф-повара""" 125 | 126 | def __init__(self, name: str, mediator: IMediator): 127 | super().__init__(name, mediator) 128 | 129 | def take_order(self, order: Order): 130 | self.orders.append(order) 131 | print(f"Шеф {self.name} принял {order}") 132 | 133 | def finish_order(self, order: Order): 134 | print(f"Шеф {self.name} выполнил {order}") 135 | self.mediator.notify(self, order, Event.FINISH_ORDER) 136 | 137 | def processing_order(self): 138 | if self.orders: 139 | order = self.orders.pop() 140 | print(f"Шеф {self.name} выполняет {order}") 141 | self.finish_order(order) 142 | else: 143 | print(f"Шеф {self.name} от нечего делать шинкует капусту") 144 | 145 | def type(self) -> WorkerType: 146 | return WorkerType.CHIEF 147 | 148 | 149 | class WorkersMediator(IMediator): 150 | """Посредник обмена сообщениями между 151 | работниками пиццерии""" 152 | 153 | def __init__(self): 154 | self.workers = {WorkerType.WAITER: [], 155 | WorkerType.BARMAN: [], 156 | WorkerType.CHIEF: []} 157 | 158 | def add_worker(self, worker: Worker): 159 | if worker not in self.workers[worker.type()]: 160 | self.workers[worker.type()].append(worker) 161 | 162 | def remove_worker(self, worker: Worker): 163 | if worker in self.workers[worker.type()]: 164 | self.workers[worker.type()].remove(worker) 165 | if len(self.workers[worker.type()]) == 0: 166 | print(f"Внимание работники типа {worker.type().name} " 167 | f" отсутствуют!!!") 168 | 169 | def notify(self, worker: Worker, order: Order, event: Event): 170 | if (event is Event.GET_ORDER and 171 | worker.type() is WorkerType.WAITER): 172 | if order.type is OrderType.FOOD: 173 | chef: Chief = choice(self.workers[WorkerType.CHIEF]) 174 | chef.take_order(order) 175 | else: 176 | barman: Barman = choice(self.workers[WorkerType.BARMAN]) 177 | barman.take_order(order) 178 | elif (event is Event.FINISH_ORDER and 179 | (worker.type() is WorkerType.BARMAN or 180 | worker.type() is WorkerType.CHIEF)): 181 | for waiter in self.workers[WorkerType.WAITER]: 182 | if order.id in waiter.get_orders_id(): 183 | waiter.finish_order(order) 184 | break 185 | else: 186 | print(f"{order} не был доставлен клиенту!!!") 187 | else: 188 | raise NotImplemented("Это что ещё за зверь? Оо") 189 | 190 | 191 | if __name__ == "__main__": 192 | mediator = WorkersMediator() 193 | waiter1 = Waiter("Александр", mediator) 194 | waiter2 = Waiter("Георгий", mediator) 195 | waiter3 = Waiter("Максим", mediator) 196 | barmen1 = Barman("Герман", mediator) 197 | barmen2 = Barman("Алексей", mediator) 198 | chief = Chief("Станислав", mediator) 199 | 200 | orders = [Order(choice([OrderType.FOOD, OrderType.BINGE])) 201 | for _ in range(10)] 202 | for it in orders: 203 | print("*"*30) 204 | choice([waiter1, waiter2, waiter3]).take_order(it) 205 | print("*" * 39) 206 | print("*"*8 + "Шеф-повар готовит блюда"+8*"*") 207 | print("*" * 39) 208 | for it in range(6): 209 | chief.processing_order() 210 | print("*" * 30) 211 | print("*" * 42) 212 | print("*"*8 + "Бармены смешивают коктейли"+8*"*") 213 | print("*" * 42) 214 | for it in range(7): 215 | choice([barmen1, barmen2]).processing_order() 216 | print("*" * 30) 217 | 218 | 219 | -------------------------------------------------------------------------------- /behavioral/memento.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | 4 | class Memento: 5 | """Класс Хранитель, фиксирующий текущее 6 | состояние наличия ингридиентов в пицце""" 7 | def __init__(self, state: List[str]): 8 | self.__state = state 9 | 10 | def get_state(self) -> List[str]: 11 | return self.__state[:] 12 | 13 | 14 | class Pizza: 15 | """Класс приготовляемой шеф-поваром пиццы""" 16 | def __init__(self): 17 | self.__state: List[str] = ['base'] 18 | 19 | def add_ingredient(self, ingredient: str) -> None: 20 | print(f"В пиццу добавлен ингредиент: {ingredient}") 21 | self.__state.append(ingredient) 22 | 23 | def create_memento(self): 24 | return Memento(self.__state[:]) 25 | 26 | def set_memento(self, memento: Memento): 27 | self.__state = memento.get_state() 28 | 29 | def __str__(self): 30 | return f"Текущее состояние пиццы: {self.__state}" 31 | 32 | 33 | class Chief: 34 | def __init__(self, pizza: Pizza): 35 | self.pizza = pizza 36 | self.pizza_states: List[Memento] = [] 37 | 38 | def add_ingredient_to_pizza(self, ingredient: str): 39 | self.pizza_states.append(self.pizza.create_memento()) 40 | self.pizza.add_ingredient(ingredient) 41 | 42 | 43 | def undo_add_ingredient(self): 44 | if len(self.pizza_states) == 1: 45 | self.pizza.set_memento(self.pizza_states[0]) 46 | print("Пицца вернулась в своё исходное состояние!") 47 | print(self.pizza) 48 | else: 49 | print("Отмена предыдущего действия") 50 | state = self.pizza_states.pop() 51 | self.pizza.set_memento(state) 52 | print(self.pizza) 53 | 54 | 55 | if __name__ == "__main__": 56 | pizza = Pizza() 57 | chief = Chief(pizza) 58 | print(pizza) 59 | print("*" * 8 + "Добавляем ингридиенты в пиццу" + 8 * "*") 60 | chief.add_ingredient_to_pizza('соус') 61 | chief.add_ingredient_to_pizza('оливки') 62 | chief.add_ingredient_to_pizza('салями') 63 | chief.add_ingredient_to_pizza('сыр') 64 | print(pizza) 65 | print("*" * 4 + "Отменяем произведенные ранее действия" + 4 * "*") 66 | chief.undo_add_ingredient() 67 | chief.undo_add_ingredient() 68 | chief.undo_add_ingredient() 69 | chief.undo_add_ingredient() 70 | print("*" * 5 + "Вновь добавляем ингридиенты в пиццу" + 5 * "*") 71 | chief.add_ingredient_to_pizza('соус') 72 | chief.add_ingredient_to_pizza('4 сыра') 73 | print(pizza) 74 | 75 | -------------------------------------------------------------------------------- /behavioral/observer.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | from enum import Enum 3 | from random import choice 4 | from typing import List 5 | 6 | 7 | class OrderType(Enum): 8 | """Типы возможных заказов""" 9 | CAPPUCCINO = 1 10 | LATTE = 2 11 | ESPRESSO = 3 12 | 13 | 14 | class Order: 15 | """Класс заказа""" 16 | order_id: int = 1 17 | 18 | def __init__(self, order_type: OrderType): 19 | self.id = Order.order_id 20 | self.type = order_type 21 | Order.order_id += 1 22 | 23 | def __str__(self): 24 | return f"заказ под #{self.id} ({self.type.name})" 25 | 26 | 27 | class Observer(ABC): 28 | @abstractmethod 29 | def update(self, order_id: int): 30 | ... 31 | 32 | 33 | class Subject(ABC): 34 | def __init__(self): 35 | self._observers: List[Observer] = [] 36 | 37 | def attach(self, observer: Observer) -> None: 38 | if observer not in self._observers: 39 | self._observers.append(observer) 40 | 41 | def detach(self, observer: Observer) -> None: 42 | self._observers.remove(observer) 43 | 44 | def notify(self, order_id: int) -> None: 45 | for observer in self._observers: 46 | observer.update(order_id) 47 | 48 | 49 | class Barista(Subject): 50 | def __init__(self): 51 | super().__init__() 52 | self.__orders: List[Order] = [] 53 | self.__finish_order: List[Order] = [] 54 | 55 | def take_order(self, order: Order) -> None: 56 | print(f"Бариста принял {order}") 57 | self.__orders.append(order) 58 | 59 | def get_order(self, order_id: int) -> Order: 60 | order = None 61 | for it in self.__finish_order: 62 | if it.id == order_id: 63 | order = it 64 | break 65 | self.__finish_order.remove(order) 66 | return order 67 | 68 | def processing_order(self): 69 | if self.__orders: 70 | order = choice(self.__orders) 71 | self.__orders.remove(order) 72 | self.__finish_order.append(order) 73 | print(f"Бариста выполнил {order}") 74 | self.notify(order.id) 75 | else: 76 | print("Бариста от нечего делать натирает кофемашину") 77 | 78 | 79 | class Client(Observer): 80 | def __init__(self, name: str, barista: Barista): 81 | self.__barista = barista 82 | self.__name = name 83 | self.order: Order = None 84 | 85 | def update(self, order_id: int) -> None: 86 | """принимаем номер текущего выполненного заказа 87 | и отписываемся от оповещения баристы""" 88 | if self.order is not None: 89 | if order_id == self.order.id: 90 | print(f"Клиент {self.__name} забрал(а) " 91 | f"{self.__barista.get_order(order_id)}") 92 | self.__barista.detach(self) 93 | 94 | def create_order(self) -> None: 95 | """Метод для формирования заказа 96 | и подписки на оповещения от баристы 97 | по выполненному заказу""" 98 | order_type = choice([OrderType.LATTE, 99 | OrderType.CAPPUCCINO, 100 | OrderType.ESPRESSO]) 101 | self.order = Order(order_type) 102 | print(f"Клиент {self.__name} сделал(а) {self.order}") 103 | self.__barista.attach(self) 104 | self.__barista.take_order(self.order) 105 | 106 | 107 | if __name__ == "__main__": 108 | names = ['Анастасия', 'Анна', 109 | 'Роман', 'Ростислав', 'Руслан'] 110 | barista = Barista() 111 | clients = [Client(name, barista) for name in names] 112 | for client in clients: 113 | print("*"*30) 114 | client.create_order() 115 | print("*" * 4 + "Бариста приступает к выполнению заказов" + 4 * "*") 116 | for _ in range(6): 117 | print("*"*30) 118 | barista.processing_order() -------------------------------------------------------------------------------- /behavioral/python_style_iterator.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | from collections.abc import Iterable, Iterator 3 | 4 | 5 | class PizzaItem: 6 | def __init__(self, number): 7 | self.number = number 8 | 9 | def __str__(self): 10 | return f"кусочек пиццы под номером: {self.number}" 11 | 12 | 13 | class PizzaSliceIterator(Iterator): 14 | def __init__(self, pizza: List[PizzaItem], 15 | reverse: bool = False): 16 | self._pizza = pizza 17 | self._index: int = -1 if reverse else 0 18 | self._reverse = reverse 19 | 20 | def __next__(self) -> PizzaItem: 21 | try: 22 | pizza_item = self._pizza[self._index] 23 | self._index += -1 if self._reverse else 1 24 | except IndexError: 25 | raise StopIteration() 26 | return pizza_item 27 | 28 | 29 | class PizzaAggregate(Iterable): 30 | def __init__(self, amount_slices: int = 10): 31 | self._slices = [PizzaItem(it+1) for it in range(amount_slices)] 32 | print(f"Приготовили пиццу и порезали " 33 | f"на {amount_slices} кусочков") 34 | 35 | def amount_slices(self) -> int: 36 | return len(self._slices) 37 | 38 | def __iter__(self) -> PizzaSliceIterator: 39 | return PizzaSliceIterator(self._slices) 40 | 41 | def get_reverse_iterator(self) -> PizzaSliceIterator: 42 | return PizzaSliceIterator(self._slices, True) 43 | 44 | 45 | if __name__ == "__main__": 46 | pizza = PizzaAggregate(5) 47 | for item in pizza: 48 | print("Это " + str(item)) 49 | print("*" * 8 + "Обход в обратную сторону" + "*"*8) 50 | iterator = pizza.get_reverse_iterator() 51 | for item in iterator: 52 | print("Это " + str(item)) 53 | 54 | -------------------------------------------------------------------------------- /behavioral/state.py: -------------------------------------------------------------------------------- 1 | from typing import Dict 2 | from abc import ABC, abstractmethod 3 | from enum import Enum 4 | 5 | 6 | class CoffeeState(Enum): 7 | """Возможные состояния кофе-машины""" 8 | IDLE = 0 9 | CHOOSE = 1 10 | CAPPUCCINO = 2 11 | LATTE = 3 12 | ESPRESSO = 4 13 | CHANGE_MONEY = 5 14 | 15 | 16 | class State(ABC): 17 | """Базовый класс состояния, 18 | определяющий интерфейс""" 19 | 20 | @abstractmethod 21 | def insert_money(self, coffee_machine) -> None: 22 | ... 23 | 24 | @abstractmethod 25 | def eject_money(self, coffee_machine) -> None: 26 | ... 27 | 28 | @abstractmethod 29 | def make_coffee(self, coffee_machine) -> None: 30 | ... 31 | 32 | 33 | class IdleState(State): 34 | """Состояние ожидания""" 35 | def insert_money(self, coffee_machine) -> None: 36 | print("Переходим к состоянию выбора коффе") 37 | coffee_machine.set_state(CoffeeState.CHOOSE) 38 | 39 | def eject_money(self, coffee_machine) -> None: 40 | print("Какие такие деньги? оО") 41 | 42 | def make_coffee(self, coffee_machine) -> None: 43 | print("Говорят на халяву и уксус сладок...") 44 | 45 | 46 | class WaitChooseState(State): 47 | """Состояние выбора приготовляемого коффе""" 48 | def insert_money(self, coffee_machine) -> None: 49 | print("Загружено достаточно средств для заказа?") 50 | 51 | def eject_money(self, coffee_machine) -> None: 52 | print("Заказывай или оставь свои деньги!") 53 | 54 | def make_coffee(self, coffee_machine) -> None: 55 | if coffee_machine.next_state is None: 56 | print("Выберете какой кофе хотите приготовить!") 57 | else: 58 | coffee_machine.set_state(coffee_machine.next_state) 59 | 60 | 61 | class ChangeState(State): 62 | """Состояние выбора приготовляемого коффе""" 63 | def insert_money(self, coffee_machine) -> None: 64 | self.eject_money(coffee_machine) 65 | 66 | def eject_money(self, coffee_machine) -> None: 67 | print(f"Возврат {coffee_machine.money} рублей") 68 | coffee_machine.money = 0 69 | coffee_machine.set_state(CoffeeState.IDLE) 70 | 71 | def make_coffee(self, coffee_machine) -> None: 72 | self.eject_money(coffee_machine) 73 | 74 | 75 | class CappuccinoState(State): 76 | """Состояние приготовления купучино""" 77 | 78 | def insert_money(self, coffee_machine) -> None: 79 | self.make_coffee(coffee_machine) 80 | 81 | def eject_money(self, coffee_machine) -> None: 82 | print("Не дождешься!!!") 83 | 84 | def make_coffee(self, coffee_machine) -> None: 85 | cost = 32 86 | water = 0.3 87 | milk = 0.1 88 | if coffee_machine.money >= cost: 89 | if (coffee_machine.water >= water and 90 | coffee_machine.milk >= milk): 91 | print("Готовим Капучино!") 92 | coffee_machine.water -= water 93 | coffee_machine.milk -= milk 94 | coffee_machine.money -= cost 95 | else: 96 | print("Не хватает ингридиентов!") 97 | if coffee_machine.money > 0: 98 | coffee_machine.set_state(CoffeeState.CHANGE_MONEY) 99 | coffee_machine.return_money() 100 | else: 101 | coffee_machine.set_state(CoffeeState.IDLE) 102 | else: 103 | print("Недостаточно средств для приготовления!") 104 | 105 | 106 | class LatteState(State): 107 | """Состояние приготовления латте""" 108 | 109 | def insert_money(self, coffee_machine) -> None: 110 | self.make_coffee(coffee_machine) 111 | 112 | def eject_money(self, coffee_machine) -> None: 113 | print("Не дождешься!!!") 114 | 115 | def make_coffee(self, coffee_machine) -> None: 116 | cost = 40 117 | water = 0.3 118 | milk = 0.2 119 | if coffee_machine.money >= cost: 120 | if (coffee_machine.water >= water and 121 | coffee_machine.milk >= milk): 122 | print("Готовим Латте!") 123 | coffee_machine.water -= water 124 | coffee_machine.milk -= milk 125 | coffee_machine.money -= cost 126 | else: 127 | print("Не хватает ингридиентов!") 128 | if coffee_machine.money > 0: 129 | coffee_machine.set_state(CoffeeState.CHANGE_MONEY) 130 | coffee_machine.return_money() 131 | else: 132 | coffee_machine.set_state(CoffeeState.IDLE) 133 | else: 134 | print("Недостаточно средств для приготовления!") 135 | 136 | 137 | class EspressoState(State): 138 | """Состояние приготовления espresso'""" 139 | 140 | def insert_money(self, coffee_machine) -> None: 141 | self.make_coffee(coffee_machine) 142 | 143 | def eject_money(self, coffee_machine) -> None: 144 | print("Не дождешься!!!") 145 | 146 | def make_coffee(self, coffee_machine) -> None: 147 | cost = 25 148 | water = 0.3 149 | if coffee_machine.money >= cost: 150 | if coffee_machine.water >= water: 151 | print("Готовим espresso!") 152 | coffee_machine.water -= water 153 | coffee_machine.money -= cost 154 | else: 155 | print("Не хватает ингридиентов!") 156 | if coffee_machine.money > 0: 157 | coffee_machine.set_state(CoffeeState.CHANGE_MONEY) 158 | coffee_machine.return_money() 159 | else: 160 | coffee_machine.set_state(CoffeeState.IDLE) 161 | else: 162 | print("Недостаточно средств для приготовления!") 163 | 164 | 165 | class CoffeeMachine: 166 | """Класс кофе-машины""" 167 | def __init__(self, water: float, milk: float): 168 | self.water = water 169 | self.milk = milk 170 | self.money: int = 0 171 | self.__states: Dict[CoffeeState, State] = { 172 | CoffeeState.IDLE: IdleState(), 173 | CoffeeState.CHOOSE: WaitChooseState(), 174 | CoffeeState.CAPPUCCINO: CappuccinoState(), 175 | CoffeeState.LATTE: LatteState(), 176 | CoffeeState.ESPRESSO: EspressoState(), 177 | CoffeeState.CHANGE_MONEY: ChangeState(), 178 | } 179 | self.__state: State = self.__states[CoffeeState.IDLE] 180 | self.next_state: CoffeeState = None 181 | 182 | def get_state(self, state: CoffeeState): 183 | return self.__states[state] 184 | 185 | def set_state(self, state: CoffeeState): 186 | self.__state = self.__states[state] 187 | 188 | def insert_money(self, money: int) -> None: 189 | self.money += money 190 | print(f"Внесено {self.money} рублей") 191 | self.__state.insert_money(self) 192 | 193 | 194 | def cappuccino(self) -> None: 195 | print(f"Выбран режим приготовления Капучино") 196 | self.next_state = CoffeeState.CAPPUCCINO 197 | self.__state.make_coffee(self) 198 | 199 | def espresso(self) -> None: 200 | print(f"Выбран режим приготовления Espresso") 201 | self.next_state = CoffeeState.ESPRESSO 202 | self.__state.make_coffee(self) 203 | 204 | def latte(self) -> None: 205 | print(f"Выбран режим приготовления Латте") 206 | self.next_state = CoffeeState.LATTE 207 | self.__state.make_coffee(self) 208 | 209 | def make_coffee(self): 210 | print("Запуск приготовления выбранного кофе!") 211 | self.__state.make_coffee(self) 212 | 213 | def return_money(self): 214 | self.__state.eject_money(self) 215 | 216 | 217 | if __name__ == "__main__": 218 | coffee_machine = CoffeeMachine(1.0, 1.0) 219 | coffee_machine.make_coffee() 220 | coffee_machine.insert_money(10) 221 | coffee_machine.insert_money(10) 222 | coffee_machine.cappuccino() 223 | coffee_machine.make_coffee() 224 | coffee_machine.insert_money(20) 225 | print("**** Когда мало продуктов для приготовления кофе ****") 226 | coffee_machine = CoffeeMachine(0.1, 0.1) 227 | coffee_machine.insert_money(100) 228 | coffee_machine.make_coffee() 229 | coffee_machine.latte() 230 | coffee_machine.make_coffee() 231 | -------------------------------------------------------------------------------- /behavioral/strategy.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | from enum import Enum 3 | 4 | 5 | class ChiefMood(Enum): 6 | """Настроение начальника""" 7 | GOOD = 1 8 | BAD = 2 9 | BETTER_STAY_AWAY = 3 10 | 11 | 12 | class Strategy(ABC): 13 | """Интерфейс стратегии""" 14 | 15 | @abstractmethod 16 | def check_mood_chief(self, mood: ChiefMood) -> bool: 17 | ... 18 | 19 | @abstractmethod 20 | def order_processing(self, money: int) -> str: 21 | ... 22 | 23 | 24 | class GoodStrategy(Strategy): 25 | 26 | def check_mood_chief(self, mood: ChiefMood) -> bool: 27 | if (mood is ChiefMood.GOOD or 28 | mood is ChiefMood.BAD): 29 | return True 30 | return False 31 | 32 | def order_processing(self, money: int) -> str: 33 | return "Самый лучшый напиток, который возможен!" 34 | 35 | 36 | class BadStrategy(Strategy): 37 | 38 | def check_mood_chief(self, mood: ChiefMood) -> bool: 39 | if (mood is ChiefMood.BETTER_STAY_AWAY or 40 | mood is ChiefMood.BAD): 41 | return True 42 | return False 43 | 44 | def order_processing(self, money: int) -> str: 45 | return "И стакан воды сойдет!" 46 | 47 | 48 | class NormalStrategy(Strategy): 49 | 50 | def check_mood_chief(self, mood: ChiefMood) -> bool: 51 | # может у шефа и плохое настроение 52 | # но клиенты то тут не при чем 53 | return True 54 | 55 | def order_processing(self, money: int) -> str: 56 | if money < 5: 57 | return "Вежливо отказаться от заказа клиента" 58 | elif money < 10: 59 | return "Приготовить espresso" 60 | elif money < 20: 61 | return "Приготовить капучино" 62 | elif money < 50: 63 | return "Приготовить отменный кофе" 64 | else: 65 | return "Самый лучшый напиток, который возможен!" 66 | 67 | 68 | class Barista: 69 | def __init__(self, strategy: Strategy, 70 | chief_mood: ChiefMood): 71 | self._strategy = strategy 72 | self._chief_mood = chief_mood 73 | print(f"Изначальное настроение шефа: {chief_mood.name}") 74 | 75 | def get_chief_mood(self) -> ChiefMood: 76 | return self._chief_mood 77 | 78 | def set_chief_mood(self, chief_mood: ChiefMood) -> None: 79 | print(f"Текущее настроение шефа: {chief_mood.name}") 80 | self._chief_mood = chief_mood 81 | 82 | def set_strategy(self, strategy: Strategy) -> None: 83 | self._strategy = strategy 84 | 85 | def take_order(self, money: int) -> None: 86 | print(f"Клиент отдает за заказ {money} рублей") 87 | if self._strategy.check_mood_chief(self._chief_mood): 88 | print(self._strategy.order_processing(money)) 89 | else: 90 | print("Сделать вид, что не заметил клиента!") 91 | 92 | 93 | if __name__ == "__main__": 94 | barista = Barista(NormalStrategy(), 95 | ChiefMood.BETTER_STAY_AWAY) 96 | barista.take_order(20) 97 | barista.take_order(50) 98 | barista.set_strategy(BadStrategy()) 99 | barista.take_order(40) 100 | barista.take_order(200) 101 | barista.set_strategy(GoodStrategy()) 102 | barista.take_order(40) 103 | barista.set_chief_mood(ChiefMood.GOOD) 104 | barista.take_order(0) -------------------------------------------------------------------------------- /behavioral/template_method.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | from typing import List 3 | 4 | 5 | class Pizza: 6 | """Класс приготовляемой шеф-поваром пиццы""" 7 | def __init__(self): 8 | self.__state: List[str] = ['base'] 9 | 10 | def add_ingredient(self, ingredient: str) -> None: 11 | print(f"В пиццу добавлен ингредиент: {ingredient}") 12 | self.__state.append(ingredient) 13 | 14 | def __str__(self): 15 | return f"Ингридиенты пиццы: {self.__state}" 16 | 17 | 18 | class PizzaMaker(ABC): 19 | """Базовый класс шаблонного метода""" 20 | def make_pizza(self, pizza: Pizza) -> None: 21 | self.prepare_sauce(pizza) 22 | self.prepare_topping(pizza) 23 | self.cook(pizza) 24 | 25 | @abstractmethod 26 | def prepare_sauce(self, pizza: Pizza) -> None: 27 | ... 28 | 29 | @abstractmethod 30 | def prepare_topping(self, pizza: Pizza) -> None: 31 | ... 32 | 33 | @abstractmethod 34 | def cook(self, pizza: Pizza) -> None: 35 | ... 36 | 37 | 38 | class MargaritaMaker(PizzaMaker): 39 | """Класс приготовления пиццы Маргарита""" 40 | def prepare_sauce(self, pizza: Pizza) -> None: 41 | pizza.add_ingredient('Tomato') 42 | 43 | def prepare_topping(self, pizza: Pizza) -> None: 44 | pizza.add_ingredient('Bacon') 45 | pizza.add_ingredient('Mozzarella') 46 | pizza.add_ingredient('Mozzarella') 47 | 48 | def cook(self, pizza: Pizza) -> None: 49 | print("Пицца 'Маргарита' будет готова через 10 минут") 50 | 51 | 52 | class SalamiMaker(PizzaMaker): 53 | """Класс приготовления пиццы Маргарита""" 54 | def prepare_sauce(self, pizza: Pizza) -> None: 55 | pizza.add_ingredient('Pesto') 56 | 57 | def prepare_topping(self, pizza: Pizza) -> None: 58 | pizza.add_ingredient('Salami') 59 | pizza.add_ingredient('Salami') 60 | pizza.add_ingredient('Mozzarella') 61 | 62 | def cook(self, pizza: Pizza) -> None: 63 | print("Пицца 'Салями' будет готова через 15 минут") 64 | 65 | 66 | class Chief: 67 | """Класс шеф-повара""" 68 | def __init__(self, template_pizza: PizzaMaker): 69 | self.__cook = template_pizza 70 | 71 | def set_cook_template(self, template_pizza: PizzaMaker): 72 | self.__cook = template_pizza 73 | 74 | def make_pizza(self) -> Pizza: 75 | pizza = Pizza() 76 | self.__cook.make_pizza(pizza) 77 | return pizza 78 | 79 | 80 | if __name__ == "__main__": 81 | chief = Chief(MargaritaMaker()) 82 | print("*"*8 + "Готовим пиццу 'Маргарита'"+8*"*") 83 | print(chief.make_pizza()) 84 | print("*" * 8 + "Готовим пиццу 'Салями'" + 8 * "*") 85 | chief.set_cook_template(SalamiMaker()) 86 | print(chief.make_pizza()) 87 | -------------------------------------------------------------------------------- /behavioral/visitor.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | from typing import List 3 | 4 | 5 | class OrderItemVisitor(ABC): 6 | """Интерфейс посетителя""" 7 | @abstractmethod 8 | def visit(self, item) -> float: 9 | ... 10 | 11 | 12 | class ItemElement(ABC): 13 | """Интерфейс для заказываемых продуктов""" 14 | @abstractmethod 15 | def accept(self, visitor: OrderItemVisitor) -> float: 16 | ... 17 | 18 | 19 | class Pizza(ItemElement): 20 | """Класс заказываемой пиццы""" 21 | def __init__(self, name: str, price: float): 22 | self.name = name 23 | self.price = price 24 | 25 | def get_price(self) -> float: 26 | return self.price 27 | 28 | def accept(self, visitor: OrderItemVisitor) -> float: 29 | return visitor.visit(self) 30 | 31 | 32 | class Coffee(ItemElement): 33 | """Класс заказываемого кофе""" 34 | def __init__(self, name: str, 35 | price: float, 36 | capacity: float): 37 | self.name = name 38 | self.price = price # цена за литр кофе) 39 | self.capacity = capacity 40 | 41 | def get_price(self) -> float: 42 | return self.price 43 | 44 | def get_capacity(self) -> float: 45 | return self.capacity 46 | 47 | def accept(self, visitor: OrderItemVisitor) -> float: 48 | return visitor.visit(self) 49 | 50 | 51 | class WithOutDiscountVisitor(OrderItemVisitor): 52 | """Посчитываем сумму заказа с 53 | без учета скидки""" 54 | def visit(self, item: ItemElement) -> float: 55 | cost = 0 56 | if isinstance(item, Pizza): 57 | cost = item.get_price() 58 | elif isinstance(item, Coffee): 59 | cost = item.get_capacity() * item.get_price() 60 | return cost 61 | 62 | 63 | class OnlyPizzaDiscountVisitor(OrderItemVisitor): 64 | """Посчитываем сумму заказа с 65 | учетом скидки на всю пиццу в 15%""" 66 | def visit(self, item: ItemElement) -> float: 67 | cost = 0 68 | if isinstance(item, Pizza): 69 | cost = item.get_price() 70 | cost -= cost * 0.15 71 | elif isinstance(item, Coffee): 72 | cost = item.get_capacity() * item.get_price() 73 | return cost 74 | 75 | 76 | class OnlyCoffeeDiscountVisitor(OrderItemVisitor): 77 | """Посчитываем сумму заказа с 78 | учетом скидки на всё кофе в 35%""" 79 | def visit(self, item: ItemElement) -> float: 80 | cost = 0 81 | if isinstance(item, Pizza): 82 | cost = item.get_price() 83 | elif isinstance(item, Coffee): 84 | cost = item.get_capacity() * item.get_price() 85 | cost -= cost * 0.35 86 | return cost 87 | 88 | 89 | class AllDiscountVisitor(OrderItemVisitor): 90 | """Посчитываем сумму заказа с 91 | учетом скидки на всё в 20""" 92 | def visit(self, item: ItemElement) -> float: 93 | cost = 0 94 | if isinstance(item, Pizza): 95 | cost = item.get_price() 96 | elif isinstance(item, Coffee): 97 | cost = item.get_capacity() * item.get_price() 98 | cost -= cost * 0.20 99 | return cost 100 | 101 | 102 | class Waiter: 103 | def __init__(self, discount: OrderItemVisitor): 104 | self.order: List[ItemElement] = [] 105 | self.discount_calculator = discount 106 | 107 | def set_order(self, order: List[ItemElement]) -> None: 108 | self.order = order 109 | 110 | def set_discount(self, discount: OrderItemVisitor) -> None: 111 | self.discount_calculator = discount 112 | 113 | def calculate_finish_price(self) -> float: 114 | price = 0 115 | if self.order: 116 | for item in self.order: 117 | price += item.accept(self.discount_calculator) 118 | return price 119 | 120 | 121 | if __name__ == "__main__": 122 | order: List[ItemElement] = [Pizza("Маргарита", 12.3), 123 | Coffee("Латте", 5, 0.3), 124 | Pizza("4Сыра", 10.5), 125 | Pizza("Салями", 15.2), 126 | Coffee("Капучино", 4, 0.27)] 127 | discount = WithOutDiscountVisitor() 128 | waiter = Waiter(discount) 129 | waiter.set_order(order) 130 | print(f"Сумма заказа без учета скидок: " 131 | f"{waiter.calculate_finish_price()}") 132 | discount = OnlyPizzaDiscountVisitor() 133 | waiter.set_discount(discount) 134 | print(f"Сумма заказа c учетом скидки на пиццу: " 135 | f"{waiter.calculate_finish_price()}") 136 | discount = OnlyCoffeeDiscountVisitor() 137 | waiter.set_discount(discount) 138 | print(f"Сумма заказа c учетом скидки на коффе: " 139 | f"{waiter.calculate_finish_price()}") 140 | discount = AllDiscountVisitor() 141 | waiter.set_discount(discount) 142 | print(f"Сумма заказа c учетом скидки на всё: " 143 | f"{waiter.calculate_finish_price()}") -------------------------------------------------------------------------------- /creational/abstract_factory.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | 3 | """ 4 | Базовые классы графического пользовательского интерфейса 5 | """ 6 | 7 | 8 | class StatusBar(ABC): 9 | def __init__(self, system: str): 10 | self._system = system 11 | 12 | @abstractmethod 13 | def create(self): pass 14 | 15 | 16 | class MainMenu(ABC): 17 | def __init__(self, system: str): 18 | self._system = system 19 | 20 | @abstractmethod 21 | def create(self): pass 22 | 23 | 24 | class MainWindow(ABC): 25 | def __init__(self, system: str): 26 | self._system = system 27 | 28 | @abstractmethod 29 | def create(self): pass 30 | 31 | 32 | """ 33 | Производные классы графического пользовательского интерфейса 34 | для операционной системы Windows 35 | """ 36 | 37 | 38 | class WindowsStatusBar(StatusBar): 39 | def __init__(self): 40 | super().__init__("Windows") 41 | 42 | def create(self): 43 | print(f'Created status bar for {self._system}') 44 | 45 | 46 | class WindowsMainMenu(MainMenu): 47 | def __init__(self): 48 | super().__init__("Windows") 49 | 50 | def create(self): 51 | print(f'Created main menu for {self._system}') 52 | 53 | 54 | class WindowsMainWindow(MainWindow): 55 | def __init__(self): 56 | super().__init__("Windows") 57 | 58 | def create(self): 59 | print(f'Created MainWindow for {self._system}') 60 | 61 | 62 | """ 63 | Производные классы графического пользовательского интерфейса 64 | для операционной системы Linux 65 | """ 66 | 67 | 68 | class LinuxStatusBar(StatusBar): 69 | def __init__(self): 70 | super().__init__("Linux") 71 | 72 | def create(self): 73 | print(f'Created status bar for {self._system}') 74 | 75 | 76 | class LinuxMainMenu(MainMenu): 77 | def __init__(self): 78 | super().__init__("Linux") 79 | 80 | def create(self): 81 | print(f'Created main menu for {self._system}') 82 | 83 | 84 | class LinuxMainWindow(MainWindow): 85 | def __init__(self): 86 | super().__init__("Linux") 87 | 88 | def create(self): 89 | print(f'Created MainWindow for {self._system}') 90 | 91 | 92 | """ 93 | Базовый класс абстрактной фабрики 94 | """ 95 | 96 | 97 | class GuiAbstractFactory(ABC): 98 | @abstractmethod 99 | def getStatusBar(self) -> StatusBar: pass 100 | 101 | @abstractmethod 102 | def getMainMenu(self) -> MainMenu: pass 103 | 104 | @abstractmethod 105 | def getMainWindow(self) -> MainWindow: pass 106 | 107 | 108 | """ 109 | Производные классы абстрактной фабрики, 110 | конкретные реализации для каждой из операционных систем 111 | """ 112 | 113 | 114 | class WindowsGuiFactory(GuiAbstractFactory): 115 | def getStatusBar(self) -> StatusBar: 116 | return WindowsStatusBar() 117 | 118 | def getMainMenu(self) -> MainMenu: 119 | return WindowsMainMenu() 120 | 121 | def getMainWindow(self) -> MainWindow: 122 | return WindowsMainWindow() 123 | 124 | 125 | class LinuxGuiFactory(GuiAbstractFactory): 126 | def getStatusBar(self) -> StatusBar: 127 | return LinuxStatusBar() 128 | 129 | def getMainMenu(self) -> MainMenu: 130 | return LinuxMainMenu() 131 | 132 | def getMainWindow(self) -> MainWindow: 133 | return LinuxMainWindow() 134 | 135 | 136 | """ 137 | Клиентский класс, использующий фабрику для создания GUI 138 | """ 139 | 140 | 141 | class Application: 142 | def __init__(self, factory: GuiAbstractFactory): 143 | self._gui_factory = factory 144 | 145 | def create_gui(self): 146 | mainwindow = self._gui_factory.getMainWindow() 147 | status_bar = self._gui_factory.getStatusBar() 148 | main_menu = self._gui_factory.getMainMenu() 149 | mainwindow.create() 150 | main_menu.create() 151 | status_bar.create() 152 | 153 | 154 | def create_factory(system_name: str) -> GuiAbstractFactory: 155 | factory_dict = { 156 | "Windows": WindowsGuiFactory, 157 | "Linux": LinuxGuiFactory 158 | } 159 | return factory_dict[system_name]() 160 | 161 | if __name__ == '__main__': 162 | system_name = "Linux" 163 | ui = create_factory(system_name) 164 | app = Application(ui) 165 | app.create_gui() 166 | -------------------------------------------------------------------------------- /creational/borg.py: -------------------------------------------------------------------------------- 1 | class Borg: 2 | _shared_state = {} 3 | 4 | def __init__(self): 5 | self.__dict__ = self._shared_state 6 | 7 | 8 | class MonostateSingleton(Borg): 9 | def __init__(self): 10 | Borg.__init__(self) 11 | self.name = "MySingleton" 12 | 13 | def get_name(self) -> str: 14 | return self.name 15 | 16 | def set_name(self, name: str): 17 | self.name = name 18 | 19 | class NewSingleton(Borg): 20 | def __init__(self): 21 | Borg.__init__(self) 22 | self.name = "MySingleton" 23 | 24 | def get_name(self) -> str: 25 | return self.name 26 | 27 | def set_name(self, name: str): 28 | self.name = name 29 | 30 | if __name__ == "__main__": 31 | my_singleton1 = MonostateSingleton() 32 | print("Singleton1 name: " + my_singleton1.get_name()) 33 | my_singleton2 = MonostateSingleton() 34 | my_singleton2.set_name("New Singleton") 35 | print("Singleton2 name: " + my_singleton2.get_name()) 36 | print("Singleton1 name: " + my_singleton1.name) 37 | print(my_singleton1) 38 | print(my_singleton2) 39 | print(id(my_singleton1) == id(my_singleton2)) -------------------------------------------------------------------------------- /creational/builder_with_director.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | from enum import Enum, auto 3 | from collections import namedtuple 4 | 5 | # объявим именованный кортеж для свойств основы пиццы 6 | PizzaBase = namedtuple('PizzaBase', ['DoughDepth', 'DoughType']) 7 | 8 | 9 | class PizzaDoughDepth(Enum): 10 | THIN = auto() 11 | THICK = auto() 12 | 13 | 14 | class PizzaDoughType(Enum): 15 | WHEAT = auto() 16 | CORN = auto() 17 | RYE = auto() 18 | 19 | 20 | class PizzaSauceType(Enum): 21 | PESTO = auto() 22 | WHITE_GARLIC = auto() 23 | BARBEQUE = auto() 24 | TOMATO = auto() 25 | 26 | 27 | class PizzaTopLevelType(Enum): 28 | MOZZARELLA = auto() 29 | SALAMI = auto() 30 | BACON = auto() 31 | MUSHROOMS = auto() 32 | SHRIMPS = auto() 33 | 34 | 35 | """ 36 | Класс компонуемого продукта 37 | """ 38 | 39 | 40 | class Pizza: 41 | def __init__(self, name): 42 | self.name = name 43 | self.dough = None 44 | self.sauce = None 45 | self.topping = [] 46 | self.cooking_time = None # in minute 47 | 48 | def __str__(self): 49 | info: str = f"Pizza name: {self.name} \n" \ 50 | f"dough type: {self.dough.DoughDepth.name} & " \ 51 | f"{self.dough.DoughType.name}\n" \ 52 | f"sauce type: {self.sauce} \n" \ 53 | f"topping: {[it.name for it in self.topping]} \n" \ 54 | f"cooking time: {self.cooking_time} minutes" 55 | return info 56 | 57 | 58 | """ 59 | Абстрактный класс, задающий интерфейс строителя 60 | """ 61 | 62 | 63 | class Builder(ABC): 64 | 65 | @abstractmethod 66 | def prepare_dough(self) -> None: pass 67 | 68 | @abstractmethod 69 | def add_sauce(self) -> None: pass 70 | 71 | @abstractmethod 72 | def add_topping(self) -> None: pass 73 | 74 | @abstractmethod 75 | def get_pizza(self) -> Pizza: pass 76 | 77 | """ 78 | Реализация конкретных строителей (шеф-поваров) для сборки пицц 79 | """ 80 | 81 | 82 | class MargaritaPizzaBuilder(Builder): 83 | 84 | def __init__(self): 85 | self.pizza = Pizza("Margarita") 86 | self.pizza.cooking_time = 15 87 | 88 | def prepare_dough(self) -> None: 89 | self.pizza.dough = PizzaBase(PizzaDoughDepth.THICK, PizzaDoughType.WHEAT) 90 | 91 | def add_sauce(self) -> None: 92 | self.pizza.sauce = PizzaSauceType.TOMATO 93 | 94 | def add_topping(self) -> None: 95 | self.pizza.topping.extend( 96 | [ 97 | it for it in (PizzaTopLevelType.MOZZARELLA, 98 | PizzaTopLevelType.MOZZARELLA, 99 | PizzaTopLevelType.BACON, 100 | ) 101 | ] 102 | ) 103 | 104 | def get_pizza(self) -> Pizza: 105 | return self.pizza 106 | 107 | 108 | class SalamiPizzaBuilder(Builder): 109 | 110 | def __init__(self): 111 | self.pizza = Pizza("Salami") 112 | self.pizza.cooking_time = 10 113 | 114 | def prepare_dough(self) -> None: 115 | self.pizza.dough = PizzaBase(PizzaDoughDepth.THIN, PizzaDoughType.RYE) 116 | 117 | def add_sauce(self) -> None: 118 | self.pizza.sauce = PizzaSauceType.BARBEQUE 119 | 120 | def add_topping(self) -> None: 121 | self.pizza.topping.extend( 122 | [ 123 | it for it in (PizzaTopLevelType.MOZZARELLA, 124 | PizzaTopLevelType.SALAMI, 125 | ) 126 | ] 127 | ) 128 | 129 | def get_pizza(self) -> Pizza: 130 | return self.pizza 131 | 132 | """ 133 | Класс Director, отвечающий за процесс поэтапного приготовления пиццы 134 | """ 135 | class Director: 136 | def __init__(self): 137 | self.builder = None 138 | 139 | def set_builder(self, builder: Builder): 140 | self.builder = builder 141 | 142 | def make_pizza(self): 143 | if not self.builder: 144 | raise ValueError("Builder didn't set") 145 | self.builder.prepare_dough() 146 | self.builder.add_sauce() 147 | self.builder.add_topping() 148 | 149 | 150 | if __name__ == '__main__': 151 | director = Director() 152 | for it in (MargaritaPizzaBuilder, SalamiPizzaBuilder): 153 | builder = it() 154 | director.set_builder(builder) 155 | director.make_pizza() 156 | pizza = builder.get_pizza() 157 | print(pizza) 158 | print('---------------------------') 159 | -------------------------------------------------------------------------------- /creational/builder_without_director.py: -------------------------------------------------------------------------------- 1 | from creational.builder_with_director import (PizzaSauceType, 2 | PizzaBase, 3 | PizzaDoughDepth, 4 | PizzaDoughType, 5 | PizzaTopLevelType) 6 | """ 7 | Класс компонуемого продукта 8 | """ 9 | 10 | 11 | class Pizza: 12 | def __init__(self, builder): 13 | self.name = builder.name 14 | self.dough = builder.dough 15 | self.sauce = builder.sauce 16 | self.topping = builder.topping 17 | self.cooking_time = builder.cooking_time # in minute 18 | 19 | def __str__(self): 20 | info: str = f"Pizza name: {self.name} \n" \ 21 | f"dough type: {self.dough.DoughDepth.name} & " \ 22 | f"{self.dough.DoughType.name}\n" \ 23 | f"sauce type: {self.sauce} \n" \ 24 | f"topping: {[it.name for it in self.topping]} \n" \ 25 | f"cooking time: {self.cooking_time} minutes" 26 | return info 27 | 28 | @staticmethod 29 | def getBuilder(): 30 | return _Builder() 31 | 32 | 33 | """ 34 | Реализация строителя (шеф-поваров) для сборки пицц 35 | """ 36 | 37 | 38 | class _Builder: 39 | def set_name(self, name: str): 40 | self.name = name 41 | 42 | def set_dough(self, pizza_base: PizzaBase): 43 | self.dough = pizza_base 44 | 45 | def set_sauce(self, sauce: PizzaSauceType): 46 | self.sauce = sauce 47 | 48 | def set_topping(self, topping: list): 49 | self.topping = topping 50 | 51 | def set_cooking_time(self, time: int): 52 | self.cooking_time = time 53 | 54 | def build(self): 55 | return Pizza(self) 56 | 57 | 58 | if __name__ == "__main__": 59 | # Готовим пиццу Маргарита 60 | pizza_base = PizzaBase(PizzaDoughDepth.THICK, PizzaDoughType.WHEAT) 61 | builder = Pizza.getBuilder() 62 | builder.set_name("Margarita") 63 | builder.set_dough(pizza_base) 64 | builder.set_sauce(PizzaSauceType.TOMATO) 65 | builder.set_topping( 66 | [ 67 | it for it in (PizzaTopLevelType.MOZZARELLA, 68 | PizzaTopLevelType.MOZZARELLA, 69 | PizzaTopLevelType.BACON, 70 | ) 71 | ] 72 | ) 73 | builder.set_cooking_time(10) 74 | pizza = builder.build() 75 | print(pizza) 76 | -------------------------------------------------------------------------------- /creational/classic_singleton.py: -------------------------------------------------------------------------------- 1 | class SingletonBaseClass(type): 2 | _instances = {} 3 | 4 | def __call__(cls, *args, **kwargs): 5 | if cls not in cls._instances: 6 | cls._instances[cls] = super(SingletonBaseClass, cls).\ 7 | __call__(*args, **kwargs) 8 | return cls._instances[cls] 9 | 10 | 11 | class MySingleton(metaclass=SingletonBaseClass): 12 | def __init__(self): 13 | self.name = "Singleton" 14 | self.value_a = 3 15 | self.value_b = 5 16 | 17 | def add_a_b(self) -> int: 18 | return self.value_a+self.value_b 19 | 20 | def get_name(self) -> str: 21 | return self.name 22 | 23 | def set_name(self, name: str): 24 | self.name = name 25 | 26 | 27 | if __name__ == "__main__": 28 | my_singleton1 = MySingleton() 29 | my_singleton2 = MySingleton() 30 | print("Singleton1 name: " + my_singleton1.get_name()) 31 | my_singleton1.set_name("New Singleton") 32 | print("Singleton2 name: " + my_singleton2.get_name()) 33 | print(my_singleton1) 34 | print(my_singleton2) 35 | print(id(my_singleton1) == id(my_singleton2)) 36 | -------------------------------------------------------------------------------- /creational/factory_method.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | 4 | class PizzaType(Enum): 5 | """ 6 | Перечисление текущих рецептов пицц в пиццерии, 7 | которые можно приготовить 8 | """ 9 | MARGARITA = 0, 10 | MEXICO = 1, 11 | STELLA = 2 12 | 13 | 14 | class Pizza: 15 | """ 16 | Базовый класс для пицц, которые можно 17 | приготовить в пиццерии 18 | """ 19 | def __init__(self, price: float): 20 | self.__price = price # цена пиццы 21 | 22 | def get_price(self) -> float: 23 | return self.__price 24 | 25 | 26 | class PizzaMargarita(Pizza): 27 | def __init__(self): 28 | super().__init__(3.5) 29 | 30 | 31 | class PizzaMexico(Pizza): 32 | def __init__(self): 33 | super().__init__(17.5) 34 | 35 | 36 | class PizzaStella(Pizza): 37 | def __init__(self): 38 | super().__init__(5.5) 39 | 40 | 41 | def create_pizza(pizza_type: PizzaType) -> Pizza: 42 | """ 43 | Factory Method 44 | """ 45 | factory_dict = { 46 | PizzaType.MARGARITA: PizzaMargarita, 47 | PizzaType.MEXICO: PizzaMexico, 48 | PizzaType.STELLA: PizzaStella 49 | } 50 | return factory_dict[pizza_type]() 51 | 52 | 53 | if __name__ == '__main__': 54 | for pizza in PizzaType: 55 | my_pizza = create_pizza(pizza) 56 | print(f'Pizza type: {pizza}, price: {my_pizza.get_price()}') 57 | -------------------------------------------------------------------------------- /creational/first_lazy_initialization.py: -------------------------------------------------------------------------------- 1 | class Pizza: 2 | def __init__(self, pizza_name: str): 3 | self.name = pizza_name 4 | 5 | def __str__(self): 6 | return self.name 7 | 8 | 9 | class PizzaPlace: 10 | def __init__(self) -> None: 11 | self.pizzas = {} 12 | 13 | def get_pizza(self, pizza_name: str) -> Pizza: 14 | if pizza_name not in self.pizzas: 15 | self.pizzas[pizza_name] = Pizza(pizza_name) 16 | 17 | return self.pizzas[pizza_name] 18 | 19 | 20 | if __name__ == '__main__': 21 | my_pizza_place = PizzaPlace() 22 | print(my_pizza_place.get_pizza('Margarita')) 23 | print(my_pizza_place.get_pizza('Salami')) 24 | -------------------------------------------------------------------------------- /creational/method_chaining.py: -------------------------------------------------------------------------------- 1 | class MegaCalculation(): 2 | 3 | def __init__(self, value): 4 | self.value = value 5 | 6 | def add(self, a): 7 | self.value += a 8 | return self 9 | 10 | def mul(self, a): 11 | self.value *= a 12 | return self 13 | 14 | def pow(self, a): 15 | self.value = self.value ** a 16 | return self 17 | 18 | def sub(self, a): 19 | self.value -= a 20 | return self 21 | 22 | def div(self, a): 23 | self.value = self.value // a 24 | return self 25 | 26 | 27 | if __name__ == "__main__": 28 | my_calc = MegaCalculation(100) 29 | print(my_calc.add(100).div(20).mul(3).value) 30 | -------------------------------------------------------------------------------- /creational/method_chaining_with_builder.py: -------------------------------------------------------------------------------- 1 | from creational.builder_with_director import (PizzaSauceType, 2 | PizzaBase, 3 | PizzaDoughDepth, 4 | PizzaDoughType, 5 | PizzaTopLevelType) 6 | """ 7 | Класс компонуемого продукта 8 | """ 9 | 10 | 11 | class Pizza: 12 | def __init__(self, builder): 13 | self.name = builder.name 14 | self.dough = builder.dough 15 | self.sauce = builder.sauce 16 | self.topping = builder.topping 17 | self.cooking_time = builder.cooking_time # in minute 18 | 19 | def __str__(self): 20 | info: str = f"Pizza name: {self.name} \n" \ 21 | f"dough type: {self.dough.DoughDepth.name} & " \ 22 | f"{self.dough.DoughType.name}\n" \ 23 | f"sauce type: {self.sauce} \n" \ 24 | f"topping: {[it.name for it in self.topping]} \n" \ 25 | f"cooking time: {self.cooking_time} minutes" 26 | return info 27 | 28 | @staticmethod 29 | def getBuilder(): 30 | return _Builder() 31 | 32 | 33 | """ 34 | Реализация строителя (шеф-поваров) для сборки пицц c использованием паттерна Method Chaining 35 | """ 36 | 37 | 38 | class _Builder: 39 | def set_name(self, name: str): 40 | self.name = name 41 | return self 42 | 43 | def set_dough(self, pizza_base: PizzaBase): 44 | self.dough = pizza_base 45 | return self 46 | 47 | def set_sauce(self, sauce: PizzaSauceType): 48 | self.sauce = sauce 49 | return self 50 | 51 | def set_topping(self, topping: list): 52 | self.topping = topping 53 | return self 54 | 55 | def set_cooking_time(self, time: int): 56 | self.cooking_time = time 57 | return self 58 | 59 | def build(self): 60 | return Pizza(self) 61 | 62 | 63 | if __name__ == "__main__": 64 | # Готовим пиццу Маргарита 65 | pizza_base = PizzaBase(PizzaDoughDepth.THICK, PizzaDoughType.WHEAT) 66 | pizza = Pizza.getBuilder().\ 67 | set_name("Margarita")\ 68 | .set_dough(pizza_base)\ 69 | .set_sauce(PizzaSauceType.TOMATO)\ 70 | .set_topping( 71 | [ 72 | it for it in (PizzaTopLevelType.MOZZARELLA, 73 | PizzaTopLevelType.MOZZARELLA, 74 | PizzaTopLevelType.BACON, 75 | ) 76 | ])\ 77 | .set_cooking_time(10)\ 78 | .build() 79 | print(pizza) 80 | -------------------------------------------------------------------------------- /creational/multithread_singleton.py: -------------------------------------------------------------------------------- 1 | from threading import Lock 2 | 3 | 4 | class SingletonBaseClass(type): 5 | _instances = {} 6 | _lock: Lock = Lock() 7 | 8 | def __call__(cls, *args, **kwargs): 9 | with cls._lock: 10 | if cls not in cls._instances: 11 | instance = super().__call__(*args, **kwargs) 12 | cls._instances[cls] = instance 13 | return cls._instances[cls] 14 | -------------------------------------------------------------------------------- /creational/object_pool.py: -------------------------------------------------------------------------------- 1 | class Platter: 2 | 3 | def __init__(self, platter_id: int): 4 | self._empty = True 5 | self._platter_id = platter_id 6 | 7 | @property 8 | def get_id(self): 9 | return self._platter_id 10 | 11 | def is_empty(self): 12 | return not self._empty 13 | 14 | def set_pizza(self): 15 | self._empty = False 16 | 17 | def get_pizza(self): 18 | self.reset() 19 | 20 | def reset(self): 21 | self._empty = True 22 | 23 | def __str__(self): 24 | return f"Platter id: {self._platter_id}, empty: {self._empty}" 25 | 26 | 27 | class ObjectPool: 28 | def __init__(self, size: int): 29 | self.__pool_size = size 30 | self.__resources = [Platter(it) for it in range(size)] 31 | 32 | def getObject(self) -> Platter: 33 | if len(self.__resources) > 0: 34 | return self.__resources.pop(0) 35 | else: 36 | raise IndexError("Object pool is empty") 37 | 38 | def releaseObject(self, release_object: Platter): 39 | release_object.reset() 40 | self.__resources.append(release_object) 41 | 42 | def free_objects_amount(self): 43 | return len(self.__resources) 44 | 45 | def __str__(self): 46 | return f"Amount objects into pool: {len(self.__resources)}" 47 | 48 | 49 | if __name__ == "__main__": 50 | obj_pool = ObjectPool(3) 51 | print(obj_pool) 52 | my_platter = obj_pool.getObject() 53 | print(my_platter) 54 | print(obj_pool) 55 | my_platter.set_pizza() 56 | print(my_platter) 57 | obj_pool.releaseObject(my_platter) 58 | print(obj_pool) 59 | [obj_pool.getObject() for _ in range(4)] 60 | 61 | -------------------------------------------------------------------------------- /creational/prototype.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | from typing import List 3 | import copy 4 | from creational.builder_with_director import (PizzaSauceType, 5 | PizzaBase, 6 | PizzaDoughDepth, 7 | PizzaDoughType, 8 | PizzaTopLevelType) 9 | 10 | 11 | class IPrototype(ABC): 12 | @abstractmethod 13 | def clone(self): pass 14 | 15 | 16 | """ 17 | Класс компонуемого продукта 18 | """ 19 | class Pizza(IPrototype): 20 | def __init__(self, 21 | name, 22 | dough: PizzaBase = PizzaBase(PizzaDoughDepth.THICK, 23 | PizzaDoughType.WHEAT), 24 | sauce: PizzaSauceType = PizzaSauceType.TOMATO, 25 | topping: List[PizzaTopLevelType] = None, 26 | cooking_time: int = 10 27 | ): 28 | self.name = name 29 | self.dough = dough 30 | self.sauce = sauce 31 | self.topping = topping 32 | self.cooking_time = cooking_time # in minute 33 | 34 | def __str__(self): 35 | info: str = f"Pizza name: {self.name} \n" \ 36 | f"dough type: {self.dough.DoughDepth.name} & " \ 37 | f"{self.dough.DoughType.name}\n" \ 38 | f"sauce type: {self.sauce} \n" \ 39 | f"topping: {[it.name for it in self.topping] if self.topping is not None else 'None'} \n" \ 40 | f"cooking time: {self.cooking_time} minutes\n" \ 41 | f"-----------------------------------------" 42 | 43 | return info 44 | 45 | def clone(self): 46 | topping = self.topping.copy() if self.topping is not None else None 47 | return type(self)( 48 | self.name, 49 | self.dough, 50 | self.sauce, 51 | topping, 52 | self.cooking_time 53 | ) 54 | 55 | 56 | if __name__ == "__main__": 57 | pizza = Pizza("Margarita", topping=[PizzaTopLevelType.MOZZARELLA, 58 | PizzaTopLevelType.MOZZARELLA, 59 | PizzaTopLevelType.BACON]) 60 | 61 | print(pizza) 62 | new_pizza = pizza.clone() # клонируем объект 63 | new_pizza.name = "New_Margarita" 64 | print(new_pizza) 65 | salami_pizza = copy.deepcopy(new_pizza) 66 | salami_pizza.name = "Salami" 67 | salami_pizza.sauce = PizzaSauceType.BARBEQUE 68 | salami_pizza.topping.append(PizzaTopLevelType.SALAMI) 69 | print(salami_pizza) 70 | -------------------------------------------------------------------------------- /creational/second_lazy_initialization.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | from creational.builder_with_director import (PizzaSauceType, 4 | PizzaBase, 5 | PizzaDoughDepth, 6 | PizzaDoughType, 7 | PizzaTopLevelType) 8 | 9 | """ 10 | Класс компонуемого продукта 11 | """ 12 | 13 | 14 | class Pizza: 15 | def __init__(self, pizza_name: str): 16 | self._name = pizza_name 17 | self._dough = PizzaBase(PizzaDoughDepth.THICK, 18 | PizzaDoughType.WHEAT) 19 | self._sauce = None 20 | self._topping = None 21 | self._cooking_time = None # in minute 22 | 23 | def __str__(self): 24 | info: str = f"Pizza name: {self._name} \n" \ 25 | f"dough type: {self._dough.DoughDepth.name} & " \ 26 | f"{self._dough.DoughType.name}\n" 27 | if self._sauce is None: 28 | self._lazy_default_sauce() 29 | info += f"sauce type: {self._sauce} \n" 30 | 31 | if self._topping is None: 32 | self._lazy_default_topping() 33 | info += f"topping: {[it.name for it in self._topping]} \n" 34 | 35 | if self._cooking_time is None: 36 | self._lazy_default_time() 37 | info += f"cooking time: {self._cooking_time} minutes \n" \ 38 | f"-----------------------------------------" 39 | return info 40 | 41 | def _lazy_default_sauce(self) -> None: 42 | print('Sauce Lazy initialization') 43 | self._sauce = PizzaSauceType.PESTO 44 | 45 | def _lazy_default_topping(self) -> None: 46 | print('Topping Lazy initialization') 47 | self._topping = [PizzaTopLevelType.MOZZARELLA, 48 | PizzaTopLevelType.MOZZARELLA, 49 | PizzaTopLevelType.BACON] 50 | 51 | def _lazy_default_time(self) -> None: 52 | print('Cooking time Lazy initialization') 53 | self._cooking_time = 12 54 | 55 | def get_sauce(self) -> PizzaSauceType: 56 | if self._sauce is None: 57 | self._lazy_default_sauce() 58 | return self._sauce 59 | 60 | def get_name(self): 61 | return self._name 62 | 63 | def get_dough(self) -> PizzaBase: 64 | return self._dough 65 | 66 | def get_cooking_time(self) -> int: 67 | if self._cooking_time is None: 68 | self._lazy_default_time() 69 | return self._cooking_time 70 | 71 | def get_topping(self) -> List[PizzaTopLevelType]: 72 | if self._topping is None: 73 | self._lazy_default_topping() 74 | return self._topping 75 | 76 | 77 | if __name__ == "__main__": 78 | my_pizza = Pizza("Pikolito") 79 | print(my_pizza) 80 | 81 | new_pizza = Pizza("Bambino") 82 | print(new_pizza.get_name()) 83 | print(new_pizza.get_dough()) 84 | print(new_pizza.get_topping()) 85 | -------------------------------------------------------------------------------- /structural/adapter.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | 3 | 4 | class IOven(ABC): 5 | """Исходный интерфейс плиты, 6 | где единица измерения температуры - F""" 7 | @abstractmethod 8 | def get_temperature(self) -> float: 9 | pass 10 | 11 | @abstractmethod 12 | def set_temperature(self, t: float) -> None: 13 | pass 14 | 15 | 16 | class ICelsiusOven(ABC): 17 | """Интерфейс плиты с которым будем осуществлять 18 | работу в рамках разрабатываемой системы, 19 | где единица измерения температуры - C""" 20 | @abstractmethod 21 | def get_celsius_temperature(self) -> float: 22 | pass 23 | 24 | @abstractmethod 25 | def set_celsius_temperature(self, t: float) -> None: 26 | pass 27 | 28 | @abstractmethod 29 | def get_original_temperature(self) -> float: 30 | pass 31 | 32 | 33 | class OriginalOven(IOven): 34 | """Класс кухонной плиты, который будем адаптировать""" 35 | def __init__(self, t: float): 36 | assert t >= 32, "Мы тут не холодильник реализуем" 37 | self.temperature = t 38 | 39 | def set_temperature(self, t: float) -> None: 40 | assert t >= 32, "Печь которая может морозить? Хм... " \ 41 | "интересненько" 42 | self.temperature = t 43 | 44 | def get_temperature(self) -> float: 45 | return self.temperature 46 | 47 | 48 | class OvenAdapter(ICelsiusOven): 49 | """Адаптер, позволяющий работать с плитой, где 50 | единица измерения температуры фаренгейты в 51 | градусах цельсия""" 52 | CELSIUS_TO_FAHRENHEIT: float = 9.0/5.0 53 | FAHRENHEIT_TO_CELSIUS: float = 5.0/9.0 54 | FAHRENHEIT_ZERO: float = 32.0 55 | 56 | def __init__(self, original_stove: IOven): 57 | self.stove = original_stove 58 | self.temperature = self._init_temperature() 59 | 60 | def get_original_temperature(self) -> float: 61 | return self.stove.get_temperature() 62 | 63 | def _init_temperature(self) -> float: 64 | return (OvenAdapter.FAHRENHEIT_TO_CELSIUS * 65 | (self.stove.get_temperature() - 66 | OvenAdapter.FAHRENHEIT_ZERO)) 67 | 68 | def get_celsius_temperature(self) -> float: 69 | return self.temperature 70 | 71 | def set_celsius_temperature(self, t: float) -> None: 72 | 73 | new_temperature_stove = (OvenAdapter.CELSIUS_TO_FAHRENHEIT * t + 74 | OvenAdapter.FAHRENHEIT_ZERO) 75 | self.stove.set_temperature(new_temperature_stove) 76 | self.temperature = t 77 | 78 | if __name__ == "__main__": 79 | def print_temperature(stove: ICelsiusOven): 80 | print(f"Original temperature = {stove.get_original_temperature()}" 81 | f" F") 82 | print(f"Celsius temperature = {stove.get_celsius_temperature()}") 83 | 84 | fahrenheit_stove = OriginalOven(32) 85 | celsius_stove = OvenAdapter(fahrenheit_stove) 86 | print_temperature(celsius_stove) 87 | celsius_stove.set_celsius_temperature(180) 88 | print("----------------") 89 | print("New temperature") 90 | print("----------------") 91 | print_temperature(celsius_stove) 92 | 93 | 94 | -------------------------------------------------------------------------------- /structural/bridge.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | import time 3 | 4 | class Pizza: 5 | """Класс приццы для приготовления""" 6 | def __init__(self, name: str, cook_time: int, temperature: int): 7 | self.name = name 8 | self.cook_time = cook_time 9 | self.cook_temperature = temperature 10 | self.__isCook = False 11 | 12 | def cook(self) -> None: 13 | self.__isCook = True 14 | 15 | def isCooked(self) -> bool: 16 | return self.__isCook 17 | 18 | 19 | class IOvenImplementor(ABC): 20 | """Интерфейс для реализации печей различного типа""" 21 | @abstractmethod 22 | def warm_up(self, temperature: int) -> None: 23 | pass 24 | 25 | @abstractmethod 26 | def cool_down(self, temperature: int) -> None: 27 | pass 28 | 29 | @abstractmethod 30 | def cook_pizza(self, pizza: Pizza) -> None: 31 | pass 32 | 33 | @abstractmethod 34 | def get_temperature(self) -> int: 35 | pass 36 | 37 | @abstractmethod 38 | def get_oven_type(self) -> str: 39 | pass 40 | 41 | 42 | class ClassicOvenImplementor(IOvenImplementor): 43 | 44 | def __init__(self, temperature: int = 0): 45 | self.temperature = temperature 46 | self.type = "ClassicStove" 47 | 48 | def warm_up(self, temperature: int) -> None: 49 | # разогрев классической печи 50 | time.sleep((temperature - self.temperature)/10) 51 | print(f"Temperature warm up from {self.temperature}" 52 | f" to {temperature}") 53 | self.temperature = temperature 54 | 55 | def cool_down(self, temperature: int) -> None: 56 | # остужаем классическую печь 57 | time.sleep((self.temperature - temperature)/5) 58 | print(f"Temperature cool down from {self.temperature}" 59 | f" to {temperature}") 60 | self.temperature = temperature 61 | 62 | def cook_pizza(self, pizza: Pizza) -> None: 63 | time.sleep(pizza.cook_time/10) 64 | pizza.cook() 65 | 66 | def get_oven_type(self) -> str: 67 | return self.type 68 | 69 | def get_temperature(self) -> int: 70 | return self.temperature 71 | 72 | 73 | class ElectricalOvenImplementor(IOvenImplementor): 74 | 75 | def __init__(self, temperature: int = 0): 76 | self.temperature = temperature 77 | self.type = "ElectricalStove" 78 | 79 | def warm_up(self, temperature: int) -> None: 80 | # разогрев электрической печи 81 | time.sleep((temperature - self.temperature) / 30) 82 | print(f"Temperature warm up from {self.temperature}" 83 | f" to {temperature}") 84 | self.temperature = temperature 85 | 86 | def cool_down(self, temperature: int) -> None: 87 | # остужаем электрическую печь 88 | time.sleep((self.temperature - temperature) / 20) 89 | print(f"Temperature cool down from {self.temperature}" 90 | f" to {temperature}") 91 | self.temperature = temperature 92 | 93 | def cook_pizza(self, pizza: Pizza) -> None: 94 | time.sleep(pizza.cook_time / 10) 95 | pizza.cook() 96 | 97 | def get_oven_type(self) -> str: 98 | return self.type 99 | 100 | def get_temperature(self) -> int: 101 | return self.temperature 102 | 103 | 104 | class Oven: 105 | 106 | def __init__(self, implementor: IOvenImplementor): 107 | self.__implementor = implementor 108 | 109 | def __prepare_stove(self, temperature: int): 110 | if self.__implementor.get_temperature() > temperature: 111 | self.__implementor.cool_down(temperature) 112 | elif self.__implementor.get_temperature() < temperature: 113 | self.__implementor.warm_up(temperature) 114 | else: 115 | print("Ideal temperature") 116 | print("Oven prepared!") 117 | 118 | def cook_pizza(self, pizza: Pizza) -> None: 119 | self.__prepare_stove(pizza.cook_temperature) 120 | print(f"Cooking {pizza.name} pizza for {pizza.cook_time}" 121 | f" minutes at {pizza.cook_temperature} C") 122 | self.__implementor.cook_pizza(pizza) 123 | if pizza.isCooked(): 124 | print("Pizza is ready!!!") 125 | else: 126 | print("O_o ... some wrong ...") 127 | print("---------------------------") 128 | 129 | def change_implementor(self, implementor: IOvenImplementor) -> None: 130 | self.__implementor = implementor 131 | print("Implementor changed") 132 | 133 | def get_temperature(self) -> int: 134 | return self.__implementor.get_temperature() 135 | 136 | def get_implementor_name(self) -> str: 137 | return self.__implementor.get_oven_type() 138 | 139 | 140 | if __name__ == "__main__": 141 | first_pizza = Pizza("Margarita", 10, 220) 142 | second_pizza = Pizza("Salami", 9, 180) 143 | 144 | implementor = ClassicOvenImplementor() 145 | oven = Oven(implementor) 146 | print(f"Implementor type: {oven.get_implementor_name()}") 147 | oven.cook_pizza(first_pizza) 148 | oven.cook_pizza(second_pizza) 149 | # замена реализации 150 | new_implementor = ElectricalOvenImplementor(oven.get_temperature()) 151 | first_pizza = Pizza("Margarita", 9, 225) 152 | second_pizza = Pizza("Salami", 10, 175) 153 | oven.change_implementor(new_implementor) 154 | print(f"Implementor type: {oven.get_implementor_name()}") 155 | oven.cook_pizza(first_pizza) 156 | oven.cook_pizza(second_pizza) -------------------------------------------------------------------------------- /structural/composite.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | 3 | 4 | class IProduct(ABC): 5 | """Интерфейс продуктов 6 | входящих в пиццу""" 7 | @abstractmethod 8 | def cost(self) -> float: 9 | pass 10 | 11 | @abstractmethod 12 | def name(self) -> str: 13 | pass 14 | 15 | 16 | class Product(IProduct): 17 | """Класс продукта""" 18 | def __init__(self, name: str, cost: float): 19 | self.__cost = cost 20 | self.__name = name 21 | 22 | def cost(self) -> float: 23 | return self.__cost 24 | 25 | def name(self) -> str: 26 | return self.__name 27 | 28 | 29 | class CompoundProduct(IProduct): 30 | """Класс компонуемых продуктов""" 31 | def __init__(self, name: str): 32 | self.__name = name 33 | self.products = [] 34 | 35 | def cost(self): 36 | cost = 0 37 | for it in self.products: 38 | cost += it.cost() 39 | return cost 40 | 41 | def name(self) -> str: 42 | return self.__name 43 | 44 | def add_product(self, product: IProduct): 45 | self.products.append(product) 46 | 47 | def remove_product(self, product: IProduct): 48 | self.products.remove(product) 49 | 50 | def clear(self): 51 | self.products = [] 52 | 53 | 54 | class Pizza(CompoundProduct): 55 | """Класс пиццы""" 56 | def __init__(self, name: str): 57 | super(Pizza, self).__init__(name) 58 | 59 | def cost(self): 60 | cost = 0 61 | for it in self.products: 62 | cost_it = it.cost() 63 | print(f"Стоимость '{it.name()}' = {cost_it} тугриков") 64 | cost += cost_it 65 | print(f"Стоимость пиццы '{self.name()}' = {cost} тугриков") 66 | return cost 67 | 68 | 69 | if __name__ == "__main__": 70 | dough = CompoundProduct("тесто") 71 | dough.add_product(Product("мука", 3)) 72 | dough.add_product(Product("яйцо", 2.3)) 73 | dough.add_product(Product("соль", 1)) 74 | dough.add_product(Product("сахар", 2.1)) 75 | sauce = Product("Барбекю", 12.1) 76 | topping = CompoundProduct("топпинг") 77 | topping.add_product(Product("Дор блю", 14)) 78 | topping.add_product(Product("Пармезан", 12.3)) 79 | topping.add_product(Product("Моцарелла", 9.54)) 80 | topping.add_product(Product("Маасдам", 7.27)) 81 | pizza = Pizza("4 сыра") 82 | pizza.add_product(dough) 83 | pizza.add_product(sauce) 84 | pizza.add_product(topping) 85 | print(pizza.cost()) -------------------------------------------------------------------------------- /structural/decorator.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | 3 | 4 | class IPizzaBase(ABC): 5 | """Интерфейс декорируемого объекта""" 6 | @abstractmethod 7 | def cost(self) -> float: 8 | pass 9 | 10 | 11 | class PizzaBase(IPizzaBase): 12 | """Класс декорируемого объекта""" 13 | def __init__(self, cost): 14 | self.__cost = cost 15 | 16 | def cost(self) -> float: 17 | return self.__cost 18 | 19 | 20 | class IDecorator(IPizzaBase): 21 | """Интерфейс декоратора""" 22 | @abstractmethod 23 | def name(self) -> str: 24 | pass 25 | 26 | 27 | class PizzaMargarita(IDecorator): 28 | """На основе PizzaBase получаем 29 | пиццу 'Маргарита'""" 30 | def __init__(self, wrapped: IPizzaBase, pizza_cost: float): 31 | self.__wrapped = wrapped 32 | self.__cost = pizza_cost 33 | self.__name = "Маргарита" 34 | 35 | def cost(self) -> float: 36 | return self.__cost+self.__wrapped.cost() 37 | 38 | def name(self) -> str: 39 | return self.__name 40 | 41 | 42 | class PizzaSalami(IDecorator): 43 | """На основе PizzaBase получаем 44 | пиццу 'Салями'""" 45 | def __init__(self, wrapped: IPizzaBase, pizza_cost: float): 46 | self.__wrapped = wrapped 47 | self.__cost = pizza_cost 48 | self.__name = "Салями" 49 | 50 | def cost(self) -> float: 51 | return (self.__cost+self.__wrapped.cost())*2 52 | 53 | def name(self) -> str: 54 | return self.__name 55 | 56 | 57 | if __name__ == "__main__": 58 | def print_pizza(pizza: IDecorator) -> None: 59 | print(f"Стоимость пиццы '{pizza.name()}' = {pizza.cost()}") 60 | 61 | pizza_base = PizzaBase(3.4) 62 | print(f"Стоимость основы пиццы = {pizza_base.cost()}") 63 | margarita = PizzaMargarita(pizza_base, 10) 64 | print_pizza(margarita) 65 | salami = PizzaSalami(pizza_base, 7) 66 | print_pizza(salami) -------------------------------------------------------------------------------- /structural/facade.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | from enum import Enum 3 | 4 | ###################Menu and IClient######################## 5 | class MenuType(Enum): 6 | """Тип меню""" 7 | VEGAN = 1 8 | NOT_VEGAN = 2 9 | MIXED = 3 10 | 11 | 12 | class IMenu(ABC): 13 | """ 14 | Базовый класс, задающий 15 | интерфейс создаваемых меню 16 | """ 17 | @abstractmethod 18 | def get_name(self): 19 | pass 20 | 21 | 22 | class VeganMenu(IMenu): 23 | def get_name(self): 24 | return "Вегетарианское меню" 25 | 26 | 27 | class NotVeganMenu(IMenu): 28 | def get_name(self): 29 | return "Не вегетарианское меню" 30 | 31 | 32 | class MixedMenu(IMenu): 33 | def get_name(self): 34 | return "Смешанное меню" 35 | 36 | 37 | class IClient(ABC): 38 | """ 39 | Базовый класс, задающий 40 | интерфейс клиентов пиццерии 41 | """ 42 | @abstractmethod 43 | def request_menu(self, menu: IMenu): 44 | ... 45 | 46 | @abstractmethod 47 | def form_order(self) -> dict: 48 | ... 49 | 50 | @abstractmethod 51 | def eating_food(self): 52 | ... 53 | 54 | @abstractmethod 55 | def get_name(self): 56 | ... 57 | 58 | 59 | ##################################################### 60 | 61 | 62 | class Kitchen: 63 | """ 64 | Кухня 65 | """ 66 | def prepare_food(self): 67 | print("Заказанная еда готовится!") 68 | 69 | def call_waiter(self): 70 | print("Отдаем приготовленную еду официанту") 71 | 72 | 73 | class Waiter: 74 | """ 75 | Официант 76 | """ 77 | def take_order(self, client: IClient): 78 | print(f"Официант принял заказ клиента {client.get_name()}") 79 | 80 | def send_to_kitchen(self, kitchen: Kitchen): 81 | print("Официант отнес заказ на кухню") 82 | 83 | def serve_client(self, client: IClient): 84 | print(f"Блюда готовы, несем клиенту c именем {client.get_name()}!") 85 | 86 | 87 | class PizzeriaFacade: 88 | """ 89 | Пиццерия на основе паттерна 'Фасад' 90 | """ 91 | def __init__(self): 92 | self.kitchen = Kitchen() 93 | self.waiter = Waiter() 94 | self.menu = {MenuType.VEGAN: VeganMenu, 95 | MenuType.NOT_VEGAN: NotVeganMenu, 96 | MenuType.MIXED: MixedMenu} 97 | 98 | def get_menu(self, type_menu: MenuType) -> IMenu: 99 | return self.menu[type_menu]() 100 | 101 | def take_order(self, client: IClient): 102 | self.waiter.take_order(client) 103 | self.waiter.send_to_kitchen(self.kitchen) 104 | self.__kitchen_work() 105 | self.waiter.serve_client(client) 106 | 107 | def __kitchen_work(self): 108 | self.kitchen.prepare_food() 109 | self.kitchen.call_waiter() 110 | 111 | 112 | class Client(IClient): 113 | """ 114 | Класс клиента пиццерии 115 | """ 116 | def __init__(self, name: str): 117 | self.name = name 118 | 119 | def request_menu(self, menu: IMenu): 120 | print(f"Клиент {self.name} ознакамливается с '{menu.get_name()}'") 121 | 122 | def form_order(self) -> dict: 123 | print(f"Клиент {self.name} делает заказ") 124 | return {} 125 | 126 | def eating_food(self): 127 | print(f"Клиент {self.name} приступает к трапезе") 128 | 129 | def get_name(self): 130 | return self.name 131 | 132 | 133 | if __name__ == "__main__": 134 | pizzeria = PizzeriaFacade() 135 | client1 = Client("Иван") 136 | client2 = Client("Александр") 137 | client1.request_menu(pizzeria.get_menu(MenuType.MIXED)) 138 | pizzeria.take_order(client1) 139 | client2.request_menu(pizzeria.get_menu(MenuType.VEGAN)) 140 | pizzeria.take_order(client2) 141 | client1.eating_food() 142 | client2.eating_food() 143 | 144 | -------------------------------------------------------------------------------- /structural/flyweight.py: -------------------------------------------------------------------------------- 1 | class PizzaOrderFlyWeight: 2 | 3 | def __init__(self, shared_state): 4 | self.shared_state = shared_state 5 | 6 | def __repr__(self): 7 | return str(self.shared_state) 8 | 9 | 10 | class PizzaOrderContext: 11 | 12 | def __init__(self, unique_state, flyweight: PizzaOrderFlyWeight): 13 | self.unique_state = unique_state 14 | self.flyweight = flyweight 15 | 16 | def __repr__(self): 17 | return f"уникальное состояние: {self.unique_state} \n" \ 18 | f"разделяемое состояние: {self.flyweight}" 19 | 20 | 21 | class FlyWeightFactory: 22 | 23 | def __init__(self): 24 | self.flyweights = [] 25 | 26 | def get_flyweight(self, shared_state) -> PizzaOrderFlyWeight: 27 | 28 | flyweights = list(filter(lambda x: x.shared_state == 29 | shared_state, self.flyweights)) 30 | if flyweights: 31 | return flyweights[0] 32 | else: 33 | flyweight = PizzaOrderFlyWeight(shared_state) 34 | self.flyweights.append(flyweight) 35 | return flyweight 36 | 37 | @property 38 | def total(self): 39 | return len(self.flyweights) 40 | 41 | 42 | class PizzaOrderMaker: 43 | 44 | def __init__(self, flyweight_factory: FlyWeightFactory): 45 | self.flyweight_factory = flyweight_factory 46 | self.contexts = [] 47 | 48 | def make_pizza_order(self, unique_state, shared_state) -> PizzaOrderContext: 49 | flyweight = self.flyweight_factory.get_flyweight(shared_state) 50 | context = PizzaOrderContext(unique_state, flyweight) 51 | self.contexts.append(context) 52 | 53 | return context 54 | 55 | 56 | if __name__ == "__main__": 57 | flyweight_factory = FlyWeightFactory() 58 | pizza_maker = PizzaOrderMaker(flyweight_factory) 59 | 60 | shared_states = [(30, 'Большая пицца'), 61 | (25, 'Средняя пицца'), 62 | (10, 'Маленькая пицца')] 63 | unique_states = ['Маргарита', 'Салями', '4 сыра'] 64 | 65 | orders = [pizza_maker.make_pizza_order(x, y) 66 | for x in unique_states 67 | for y in shared_states] 68 | 69 | print("Количество созданных пицц:", len(orders)) 70 | print("Количество разделяемых объектов:", flyweight_factory.total) 71 | for index, pizza in enumerate(orders): 72 | print("-"*20) 73 | print(f"Номер пиццы в списке: {index}") 74 | print(pizza) 75 | -------------------------------------------------------------------------------- /structural/proxy.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | 3 | 4 | class PizzaOrderFlyWeight: 5 | 6 | def __init__(self, shared_state): 7 | self.shared_state = shared_state 8 | 9 | def __repr__(self): 10 | return str(self.shared_state) 11 | 12 | 13 | class PizzaOrderContext: 14 | 15 | def __init__(self, unique_state, flyweight: PizzaOrderFlyWeight): 16 | self.unique_state = unique_state 17 | self.flyweight = flyweight 18 | 19 | def __repr__(self): 20 | return f"уникальное состояние: {self.unique_state} \n" \ 21 | f"разделяемое состояние: {self.flyweight}" 22 | 23 | 24 | class FlyWeightFactory: 25 | 26 | def __init__(self): 27 | self.flyweights = [] 28 | 29 | def get_flyweight(self, shared_state) -> PizzaOrderFlyWeight: 30 | 31 | flyweights = list(filter(lambda x: x.shared_state == 32 | shared_state, self.flyweights)) 33 | if flyweights: 34 | return flyweights[0] 35 | else: 36 | flyweight = PizzaOrderFlyWeight(shared_state) 37 | self.flyweights.append(flyweight) 38 | return flyweight 39 | 40 | @property 41 | def total(self): 42 | return len(self.flyweights) 43 | 44 | 45 | class IOrder(ABC): 46 | @abstractmethod 47 | def make_pizza_order(self, unique_state, shared_state) -> PizzaOrderContext: 48 | pass 49 | 50 | 51 | class PizzaOrderMaker(IOrder): 52 | 53 | def __init__(self, flyweight_factory: FlyWeightFactory): 54 | self.flyweight_factory = flyweight_factory 55 | self.contexts = [] 56 | 57 | def make_pizza_order(self, unique_state, shared_state) -> PizzaOrderContext: 58 | flyweight = self.flyweight_factory.get_flyweight(shared_state) 59 | context = PizzaOrderContext(unique_state, flyweight) 60 | self.contexts.append(context) 61 | 62 | return context 63 | 64 | 65 | class ProxyOrderMaker(IOrder): 66 | 67 | def __init__(self, real_subject: PizzaOrderMaker): 68 | self.__real_subject = real_subject 69 | 70 | def make_pizza_order(self, unique_state, shared_state) -> PizzaOrderContext: 71 | self.__logging(unique_state, shared_state) 72 | return self.__real_subject.make_pizza_order(unique_state, shared_state) 73 | 74 | def check_access(self) -> bool: 75 | print('Проверка готовности Proxy') 76 | return self.__real_subject is not None 77 | 78 | def __logging(self, unique_state, shared_state) -> None: 79 | print(f"----Логируемые данные заказа----\n" 80 | f"уникальное состояние: {unique_state} \n" 81 | f"разделяемое состояние: {shared_state}") 82 | 83 | 84 | if __name__ == "__main__": 85 | flyweight_factory = FlyWeightFactory() 86 | pizza_maker = PizzaOrderMaker(flyweight_factory) 87 | log_proxy = ProxyOrderMaker(pizza_maker) 88 | 89 | shared_states = [(30, 'Большая пицца'), 90 | (25, 'Средняя пицца'), 91 | (10, 'Маленькая пицца')] 92 | unique_states = ['Маргарита', 'Салями', '4 сыра'] 93 | 94 | orders = [log_proxy.make_pizza_order(x, y) 95 | for x in unique_states 96 | for y in shared_states] 97 | print("#"*20) 98 | print("Количество созданных пицц:", len(orders)) 99 | print("Количество разделяемых объектов:", flyweight_factory.total) 100 | --------------------------------------------------------------------------------