Python | Вопросы собесов
13.8K subscribers
38 photos
1 file
959 links
Download Telegram
🤔 Что такое абстрактная фабрика?

Абстрактная фабрика (Abstract Factory) — это порождающий паттерн проектирования, который предоставляет интерфейс для создания семейств взаимосвязанных или взаимозависимых объектов без указания их конкретных классов. Этот паттерн позволяет создавать комплекты объектов, которые должны работать вместе, обеспечивая при этом их взаимозаменяемость.

🚩Основные концепции

🟠Абстрактная фабрика (Abstract Factory): Интерфейс или базовый класс с методами для создания различных продуктов (объектов).
🟠Конкретные фабрики (Concrete Factories): Реализации абстрактной фабрики, создающие конкретные продукты.
🟠Абстрактные продукты (Abstract Products): Интерфейсы или базовые классы для продуктов, которые создаются фабриками.
🟠Конкретные продукты (Concrete Products): Реализации абстрактных продуктов.

Создание абстрактных продуктов: Рассмотрим пример, в котором абстрактная фабрика создает различные виды кнопок и чекбоксов для двух разных операционных систем: Windows и MacOS.
from abc import ABC, abstractmethod

class Button(ABC):
@abstractmethod
def click(self):
pass

class Checkbox(ABC):
@abstractmethod
def check(self):
pass


Создание конкретных продуктов
class WindowsButton(Button):
def click(self):
print("Windows button clicked")

class MacOSButton(Button):
def click(self):
print("MacOS button clicked")

class WindowsCheckbox(Checkbox):
def check(self):
print("Windows checkbox checked")

class MacOSCheckbox(Checkbox):
def check(self):
print("MacOS checkbox checked")


Создание абстрактной фабрики
class GUIFactory(ABC):
@abstractmethod
def create_button(self) -> Button:
pass

@abstractmethod
def create_checkbox(self) -> Checkbox:
pass


Создание конкретных фабрик
class WindowsFactory(GUIFactory):
def create_button(self) -> Button:
return WindowsButton()

def create_checkbox(self) -> Checkbox:
return WindowsCheckbox()

class MacOSFactory(GUIFactory):
def create_button(self) -> Button:
return MacOSButton()

def create_checkbox(self) -> Checkbox:
return MacOSCheckbox()


Использование абстрактной фабрики
def client_code(factory: GUIFactory):
button = factory.create_button()
checkbox = factory.create_checkbox()
button.click()
checkbox.check()

# Клиентский код может работать с любыми фабриками и продуктами
windows_factory = WindowsFactory()
macos_factory = MacOSFactory()

print("Client: Testing client code with Windows factory:")
client_code(windows_factory)

print("\nClient: Testing client code with MacOS factory:")
client_code(macos_factory)


🚩Преимущества абстрактной фабрики

🟠Изоляция конкретных классов: Клиентский код работает только с интерфейсами или абстрактными классами.
🟠Легкость замены семейств продуктов: Можно легко менять фабрики, чтобы использовать другие семейства продуктов.
🟠Согласованность продуктов: Абстрактная фабрика гарантирует, что продукты одного семейства будут совместимы между собой.

🚩Недостатки абстрактной фабрики

🟠Усложнение кода: Добавление новых классов и интерфейсов может усложнить структуру кода.
🟠Трудности расширения: При добавлении нового типа продукта может потребоваться изменение интерфейсов и всех конкретных фабрик.

Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7
🤔 Что такое паттерн Строитель (Builder)?

Паттерн "Строитель" — это порождающий паттерн проектирования, который позволяет создавать сложные объекты пошагово и отделяет процесс конструирования объекта от его представления, что дает возможность использовать один и тот же процесс для создания различных представлений.

🚩Основные концепции

🟠 Строитель (Builder): Интерфейс или абстрактный класс, определяющий шаги для построения объекта.
🟠Конкретные строители (Concrete Builders): Реализуют интерфейс строителя, создавая конкретные части продукта.
🟠Продукт (Product): Сложный объект, который строится.
🟠Директор (Director): Опциональный класс, который контролирует процесс построения, используя строителя.

🚩Пример использования паттерна для создания объектов типа House.

Создание продукта
class House:
def __init__(self):
self.walls = None
self.roof = None
self.windows = None

def __str__(self):
return f"House with {self.walls} walls, {self.roof} roof, and {self.windows} windows."


Определение интерфейса строителя
from abc import ABC, abstractmethod

class HouseBuilder(ABC):
@abstractmethod
def build_walls(self):
pass

@abstractmethod
def build_roof(self):
pass

@abstractmethod
def build_windows(self):
pass

@abstractmethod
def get_house(self):
pass


Создание конкретных строителей
class ConcreteHouseBuilder(HouseBuilder):
def __init__(self):
self.house = House()

def build_walls(self):
self.house.walls = "brick"
return self

def build_roof(self):
self.house.roof = "tile"
return self

def build_windows(self):
self.house.windows = "double-glazed"
return self

def get_house(self):
return self.house


Создание директора (опционально)
class Director:
def __init__(self, builder):
self.builder = builder

def construct_house(self):
self.builder.build_walls().build_roof().build_windows()
return self.builder.get_house()


Использование паттерна "Строитель"
# Использование без директора
builder = ConcreteHouseBuilder()
house = builder.build_walls().build_roof().build_windows().get_house()
print(house) # House with brick walls, tile roof, and double-glazed windows.

# Использование с директором
director = Director(builder)
house = director.construct_house()
print(house) # House with brick walls, tile roof, and double-glazed windows.


🚩Плюсы

🟠 Упрощение создания сложных объектов: Позволяет создавать сложные объекты пошагово.
🟠Отделение процесса конструирования от представления: Разделяет логику создания объекта и его состав.
🟠 Гибкость: Один и тот же процесс построения можно использовать для создания различных представлений объекта.

🚩Минусы

🟠Усложнение кода: Добавление дополнительных классов может усложнить структуру программы.
🟠Необходимость в дополнительных классах: Для каждого типа объекта требуется отдельный класс строителя.

Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍103
🤔 Что такое Фабричный метод (Factory method)?

Это порождающий паттерн проектирования, который определяет интерфейс для создания объекта, но позволяет подклассам изменять тип создаваемого объекта. Это позволяет классу делегировать создание объектов подклассам, таким образом убирая жесткую привязку к конкретным классам и делая систему более гибкой и расширяемой.

🚩Основные концепции

🟠Продукт (Product): Определяет интерфейс для объектов, которые будут создаваться.
🟠Конкретный продукт (Concrete Product): Реализует интерфейс продукта.
🟠Создатель (Creator): Определяет фабричный метод, который возвращает объект продукта. Может содержать реализацию метода по умолчанию, возвращающую какой-то стандартный продукт.
🟠Конкретный создатель (Concrete Creator): Переопределяет фабричный метод для создания конкретного продукта.

🚩Использования фабричного метода

Определение интерфейса продукта
from abc import ABC, abstractmethod

class Animal(ABC):
@abstractmethod
def speak(self):
pass


Создание конкретных продуктов
class Dog(Animal):
def speak(self):
return "Woof!"

class Cat(Animal):
def speak(self):
return "Meow!"


Определение создателя
class AnimalFactory(ABC):
@abstractmethod
def create_animal(self) -> Animal:
pass

def some_operation(self) -> str:
animal = self.create_animal()
return f"The animal says: {animal.speak()}"


Создание конкретных создателей
class DogFactory(AnimalFactory):
def create_animal(self) -> Animal:
return Dog()

class CatFactory(AnimalFactory):
def create_animal(self) -> Animal:
return Cat()


Использование фабричного метода
def client_code(factory: AnimalFactory):
print(factory.some_operation())

dog_factory = DogFactory()
cat_factory = CatFactory()

print("Client: Testing client code with DogFactory:")
client_code(dog_factory) # Вывод: The animal says: Woof!

print("\nClient: Testing client code with CatFactory:")
client_code(cat_factory) # Вывод: The animal says: Meow!


🚩Плюсы

Изоляция конкретных классов: Клиентский код работает с интерфейсами или абстрактными классами, а не с конкретными реализациями.
Упрощение добавления новых продуктов: Легко добавлять новые виды продуктов, создавая новые конкретные создатели.
Гибкость и расширяемость: Позволяет изменять способ создания объектов без изменения клиентского кода.

🚩Минусы

Усложнение кода: Может потребоваться создание большого количества классов для каждого конкретного продукта и его создателя.
Дополнительные абстракции: Для каждого типа продукта требуется создание интерфейсов или абстрактных классов.

Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
4👍2
🤔 Что такое паттерн Прототип (Prototype)?

Это порождающий паттерн проектирования, который позволяет создавать новые объекты, копируя уже существующие экземпляры. Этот паттерн полезен, когда процесс создания объекта является дорогим (в смысле затрат ресурсов) и трудоемким, а копирование существующего объекта оказывается проще и быстрее.

🚩Основные концепции

🟠Прототип (Prototype): Интерфейс или абстрактный класс, который объявляет метод clone(), позволяющий клонировать объект.
🟠Конкретный прототип (Concrete Prototype): Реализация интерфейса прототипа, которая определяет, как должен быть клонирован объект.
🟠Клиент (Client): Объект, который создает новые объекты, вызывая метод clone() на прототипах.

🚩Плюсы

Скорость создания объектов: Клонирование объектов может быть быстрее, чем создание новых с нуля, особенно если процесс создания объектов сложен.
Уменьшение зависимости от конкретных классов: Клиенты могут работать с интерфейсом прототипа вместо работы с конкретными классами.
Упрощение добавления и удаления объектов: Легко создавать новые типы объектов путем клонирования существующих.

🚩Минусы

Сложность реализации: Реализация клонирования может быть сложной, особенно если объект имеет сложные зависимости или использует ресурсы, которые не могут быть просто скопированы.
Проблемы с глубоким и поверхностным копированием: Нужно тщательно продумать, как должны копироваться вложенные объекты (глубокое копирование или поверхностное копирование).

🚩Пример

Определение интерфейса прототипа
from abc import ABC, abstractmethod
import copy

class Shape(ABC):
def __init__(self, id):
self.id = id

@abstractmethod
def clone(self):
pass


Создание конкретных прототипов
class Rectangle(Shape):
def __init__(self, id, width, height):
super().__init__(id)
self.width = width
self.height = height

def clone(self):
return copy.deepcopy(self)

def __str__(self):
return f"Rectangle(id={self.id}, width={self.width}, height={self.height})"

class Circle(Shape):
def __init__(self, id, radius):
super().__init__(id)
self.radius = radius

def clone(self):
return copy.deepcopy(self)

def __str__(self):
return f"Circle(id={self.id}, radius={self.radius})"


Использование прототипа
def main():
rect1 = Rectangle(1, 10, 20)
circle1 = Circle(2, 15)

rect2 = rect1.clone()
circle2 = circle1.clone()

print(rect1) # Rectangle(id=1, width=10, height=20)
print(rect2) # Rectangle(id=1, width=10, height=20)
print(circle1) # Circle(id=2, radius=15)
print(circle2) # Circle(id=2, radius=15)

if __name__ == "__main__":
main()


Ставь
👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6
🤔 Что такое Структурные (Structural) паттерны?

Это паттерны проектирования, которые упрощают создание отношений между объектами, обеспечивая гибкую структуру и способствуя повторному использованию кода. Эти паттерны помогают организовать классы и объекты в большие структуры, сохраняя при этом гибкость и эффективность.

🚩Описание и примеры

🟠Адаптер (Adapter)
Паттерн Адаптер позволяет объектам с несовместимыми интерфейсами работать вместе. Он оборачивает один из объектов, преобразуя его интерфейс в совместимый.
class EuropeanSocket:
def voltage(self):
return 230

class USASocket:
def voltage(self):
return 120

class Adapter:
def __init__(self, socket):
self.socket = socket

def voltage(self):
return self.socket.voltage()

# Использование
european_socket = EuropeanSocket()
adapter = Adapter(european_socket)
print(adapter.voltage()) # 230


🟠Мост (Bridge)
Паттерн Мост разделяет абстракцию и реализацию, позволяя изменять их независимо друг от друга.
class DrawingAPI:
def draw_circle(self, x, y, radius):
pass

class DrawingAPI1(DrawingAPI):
def draw_circle(self, x, y, radius):
print(f"API1.circle at {x}:{y} radius {radius}")

class DrawingAPI2(DrawingAPI):
def draw_circle(self, x, y, radius):
print(f"API2.circle at {x}:{y} radius {radius}")

class Shape:
def __init__(self, drawing_api):
self.drawing_api = drawing_api

def draw(self):
pass

class CircleShape(Shape):
def __init__(self, x, y, radius, drawing_api):
super().__init__(drawing_api)
self.x = x
self.y = y
self.radius = radius

def draw(self):
self.drawing_api.draw_circle(self.x, self.y, self.radius)

# Использование
circle1 = CircleShape(1, 2, 3, DrawingAPI1())
circle2 = CircleShape(5, 7, 11, DrawingAPI2())
circle1.draw() # API1.circle at 1:2 radius 3
circle2.draw() # API2.circle at 5:7 radius 11


🟠Компоновщик (Composite)
Паттерн Компоновщик позволяет группировать объекты в древовидные структуры для представления иерархий "часть-целое".
class Component:
def operation(self):
pass

class Leaf(Component):
def operation(self):
return "Leaf"

class Composite(Component):
def __init__(self):
self.children = []

def add(self, component):
self.children.append(component)

def operation(self):
results = []
for child in self.children:
results.append(child.operation())
return f"Branch({'+'.join(results)})"

# Использование
leaf1 = Leaf()
leaf2 = Leaf()
composite = Composite()
composite.add(leaf1)
composite.add(leaf2)
print(composite.operation()) # Branch(Leaf+Leaf)


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5
🤔 Что такое паттерн Адаптер (Adapter)?

Паттерн "Адаптер" (Adapter) — это структурный паттерн проектирования, который позволяет объектам с несовместимыми интерфейсами работать вместе. Адаптер оборачивает один из объектов и преобразует его интерфейс в интерфейс, который ожидает клиент. Этот паттерн часто используется для интеграции кода с устаревшими библиотеками или компонентами.

🚩Основные концепции

Целевой интерфейс (Target Interface): Интерфейс, который ожидает клиент.
Клиент (Client): Объект, который использует целевой интерфейс для взаимодействия с другими объектами.
Адаптируемый интерфейс (Adaptee): Существующий интерфейс, который необходимо адаптировать.
Адаптер (Adapter): Класс, который оборачивает адаптируемый интерфейс и преобразует его в целевой интерфейс.

🚩Примеры

Определение целевого интерфейса и адаптируемого интерфейса
class EuropeanSocketInterface:
def voltage(self):
pass

def live(self):
pass

def neutral(self):
pass

def earth(self):
pass

class USASocket:
def voltage(self):
return 120

def live(self):
return 1

def neutral(self):
return -1


Создание адаптера
class Adapter(EuropeanSocketInterface):
def __init__(self, usa_socket):
self.usa_socket = usa_socket

def voltage(self):
return self.usa_socket.voltage()

def live(self):
return self.usa_socket.live()

def neutral(self):
return self.usa_socket.neutral()

def earth(self):
return 0 # В американских розетках может не быть заземления


Использование адаптера
def client_code(socket):
print(f"Voltage: {socket.voltage()}V")
print(f"Live: {socket.live()}")
print(f"Neutral: {socket.neutral()}")
print(f"Earth: {socket.earth()}")

# Использование
usa_socket = USASocket()
adapter = Adapter(usa_socket)
client_code(adapter)


🚩Плюсы

Совместимость: Позволяет объектам с несовместимыми интерфейсами работать вместе.
Простота интеграции: Упрощает интеграцию старого кода с новым, минимизируя изменения.
Гибкость: Позволяет изменять и расширять существующие классы без изменения их исходного кода.

🚩Минусы

Сложность: Может добавить дополнительный уровень абстракции, что может усложнить систему.
Зависимость: Адаптер может зависеть от конкретных деталей реализации адаптируемого интерфейса.

🚩Разновидности паттерна "Адаптер"

Объектный адаптер: Использует композицию для оборачивания объекта.
Классовый адаптер: Использует множественное наследование для адаптации одного интерфейса к другому.

Пример классового адаптера:
class USASocket:
def voltage(self):
return 120

def live(self):
return 1

def neutral(self):
return -1

class EuropeanSocketInterface:
def voltage(self):
pass

def live(self):
pass

def neutral(self):
pass

def earth(self):
pass

class Adapter(EuropeanSocketInterface, USASocket):
def earth(self):
return 0 # В американских розетках может не быть заземления

# Использование
adapter = Adapter()
client_code(adapter)


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍74
🤔 Что такое паттерн Заместитель (Proxy)?

Это структурный шаблон проектирования, который предоставляет объект, управляющий доступом к другому объекту. Этот паттерн создаёт суррогат или заместителя для другого объекта и контролирует доступ к нему.

🚩Зачем нужен паттерн Заместитель

🟠Управление доступом
Когда необходимо контролировать доступ к ресурсу.
🟠Отложенная инициализация
Когда необходимо отложить создание ресурсоёмких объектов до момента их первого использования.
🟠Управление ресурсами
Для управления ресурсами, такими как память или сетевые соединения.
🟠Логирование и кэширование
Для добавления дополнительной функциональности, такой как логирование или кэширование, без изменения кода основного объекта.

🚩Типы заместителей

🟠Управляющий заместитель (Virtual Proxy):
Контролирует доступ к объекту, создавая его по требованию.
🟠Защитный заместитель (Protection Proxy):
Контролирует доступ к объекту, ограничивая права пользователей.
🟠Удалённый заместитель (Remote Proxy)
Управляет доступом к объекту, находящемуся в другом адресном пространстве.
🟠Кэш-прокси (Cache Proxy)
Кэширует результаты запросов к объекту для повышения производительности.

🚩Как используется паттерн Заместитель

Заместитель реализует интерфейс основного объекта и перенаправляет вызовы к реальному объекту, добавляя при этом дополнительную функциональность. В этом примере класс Proxy контролирует доступ к классу RealSubject, добавляя проверку доступа и логирование.
from abc import ABC, abstractmethod

class Subject(ABC):
@abstractmethod
def request(self):
pass

class RealSubject(Subject):
def request(self):
print("Реальный объект: Обработка запроса.")

class Proxy(Subject):
def __init__(self, real_subject):
self._real_subject = real_subject

def request(self):
if self.check_access():
self._real_subject.request()
self.log_access()

def check_access(self):
print("Заместитель: Проверка доступа перед выполнением запроса.")
return True

def log_access(self):
print("Заместитель: Логирование времени запроса.")

# Клиентский код
real_subject = RealSubject()
proxy = Proxy(real_subject)

proxy.request()


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍71
🤔 Что такое поведенческие паттерны?

Поведенческие паттерны — это тип шаблонов проектирования, которые сосредоточены на взаимодействии объектов и классов. Их основная цель — упростить и улучшить коммуникацию между объектами, сделать систему более гибкой и легкой для сопровождения.

🚩Зачем нужны поведенческие паттерны?

🟠Упрощение взаимодействия объектов:
Поведенческие паттерны помогают организовать взаимодействие объектов таким образом, чтобы они могли легко и эффективно обмениваться данными и выполнять совместные задачи.
🟠Повышение гибкости системы:
Эти паттерны обеспечивают гибкость в изменении алгоритмов и методов работы объектов без изменения их классов.
🟠Поддержка принципов SOLID:
Поведенческие паттерны способствуют соблюдению принципов SOLID, особенно принципа единственной ответственности и принципа открытости/закрытости.

🚩 Примеры поведенческих паттернов

Стратегия (Strategy)
Позволяет выбирать алгоритм на основе контекста, без изменения класса, который его использует.
from abc import ABC, abstractmethod

class Strategy(ABC):
@abstractmethod
def execute(self, data):
pass

class ConcreteStrategyA(Strategy):
def execute(self, data):
return sorted(data)

class ConcreteStrategyB(Strategy):
def execute(self, data):
return sorted(data, reverse=True)

class Context:
def __init__(self, strategy: Strategy):
self._strategy = strategy

def set_strategy(self, strategy: Strategy):
self._strategy = strategy

def execute_strategy(self, data):
return self._strategy.execute(data)

data = [5, 2, 9, 1]
context = Context(ConcreteStrategyA())
print(context.execute_strategy(data)) # [1, 2, 5, 9]

context.set_strategy(ConcreteStrategyB())
print(context.execute_strategy(data)) # [9, 5, 2, 1]


Наблюдатель (Observer)
Определяет зависимость "один ко многим" между объектами таким образом, что при изменении состояния одного объекта все зависимые объекты оповещаются и обновляются автоматически.
class Subject:
def __init__(self):
self._observers = []

def add_observer(self, observer):
self._observers.append(observer)

def remove_observer(self, observer):
self._observers.remove(observer)

def notify_observers(self, message):
for observer in self._observers:
observer.update(message)

class Observer:
def update(self, message):
pass

class ConcreteObserver(Observer):
def update(self, message):
print(f"Observer received: {message}")

subject = Subject()
observer1 = ConcreteObserver()
observer2 = ConcreteObserver()

subject.add_observer(observer1)
subject.add_observer(observer2)

subject.notify_observers("Hello Observers!") # Observer received: Hello Observers!


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8
🤔 Что такое Цепочка ответственности (Chain of responsobility)?

Цепочка ответственности (Chain of Responsibility) — это поведенческий паттерн проектирования, который позволяет передавать запрос по цепочке потенциальных обработчиков, пока один из них не обработает запрос. Этот паттерн избавляет отправителя запроса от необходимости знать, какой объект в цепочке обработает его запрос.

🚩Зачем нужен паттерн Цепочка ответственности?

🟠Разделение обязанностей:
Позволяет разделить обязанности между различными обработчиками, каждый из которых решает, обработать ли запрос или передать его следующему обработчику в цепочке.
🟠Гибкость в добавлении новых обработчиков:
Обработчики могут легко добавляться или удаляться из цепочки без изменения кода других обработчиков или отправителя.
🟠Упрощение кода отправителя:
Отправитель запроса не нуждается в информации о том, какой объект обработает запрос, что снижает связанность и упрощает код.

🚩Пример использования

Рассмотрим пример, где запросы обрабатываются различными уровнями поддержки (например, базовая, средняя и расширенная поддержка).
from abc import ABC, abstractmethod

class Handler(ABC):
def __init__(self, successor=None):
self._successor = successor

@abstractmethod
def handle(self, request):
if self._successor:
return self._successor.handle(request)
return None

class BasicSupportHandler(Handler):
def handle(self, request):
if request == 'basic':
return "Basic support handled the request"
return super().handle(request)

class IntermediateSupportHandler(Handler):
def handle(self, request):
if request == 'intermediate':
return "Intermediate support handled the request"
return super().handle(request)

class AdvancedSupportHandler(Handler):
def handle(self, request):
if request == 'advanced':
return "Advanced support handled the request"
return super().handle(request)

# Создаем цепочку обработчиков
handler_chain = BasicSupportHandler(
IntermediateSupportHandler(
AdvancedSupportHandler()
)
)

# Тестируем цепочку
print(handler_chain.handle('basic')) # Basic support handled the request
print(handler_chain.handle('intermediate')) # Intermediate support handled the request
print(handler_chain.handle('advanced')) # Advanced support handled the request
print(handler_chain.handle('unknown')) # None (запрос не обработан)


🚩Как это работает

🟠Создание абстрактного класса `Handler`:
Все конкретные обработчики наследуются от него и реализуют метод handle.
🟠Реализация конкретных обработчиков:
Каждый обработчик проверяет, может ли он обработать запрос. Если может, он обрабатывает его и возвращает результат. Если не может, он передает запрос следующему обработчику в цепочке.
🟠Создание цепочки обработчиков:
Обработчики связываются друг с другом, образуя цепочку.
🟠Передача запроса: Запрос передается по цепочке, пока один из обработчиков не обработает его или пока не будет достигнут конец цепочки.

Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍101
🤔 Что такое паттерн Команда (Command)?

Паттерн Команда (Command) — это поведенческий паттерн проектирования, который превращает запросы в объекты, позволяя передавать их как аргументы при вызове методов, ставить запросы в очередь, логировать их и поддерживать отмену операций. Этот паттерн инкапсулирует запрос в виде объекта, что позволяет легко манипулировать действиями, включая отмену и повторное выполнение.

🚩Зачем нужен паттерн Команда?

🟠Инкапсуляция запроса:
Отделяет объект, инициирующий запрос, от объекта, выполняющего запрос.
🟠Поддержка отмены и повтора операций:
Позволяет реализовать операции отмены и повтора действий, так как команды хранят состояние для выполнения и отмены.
🟠Логирование и очереди запросов:
Легко логировать команды и добавлять их в очередь для последовательного выполнения.
🟠Упрощение взаимодействия объектов:
Уменьшает связанность между отправителем и получателем запроса, делая систему более гибкой.

🚩Пример использования

Рассмотрим пример реализации паттерна Команда для управления светом (включение и выключение).
from abc import ABC, abstractmethod

# Интерфейс команды
class Command(ABC):
@abstractmethod
def execute(self):
pass

@abstractmethod
def undo(self):
pass

# Получатель команды
class Light:
def on(self):
print("Light is ON")

def off(self):
print("Light is OFF")

# Конкретная команда для включения света
class LightOnCommand(Command):
def __init__(self, light: Light):
self._light = light

def execute(self):
self._light.on()

def undo(self):
self._light.off()

# Конкретная команда для выключения света
class LightOffCommand(Command):
def __init__(self, light: Light):
self._light = light

def execute(self):
self._light.off()

def undo(self):
self._light.on()

# Инициатор команды
class RemoteControl:
def __init__(self):
self._command = None

def set_command(self, command: Command):
self._command = command

def press_button(self):
if self._command:
self._command.execute()

def press_undo(self):
if self._command:
self._command.undo()

# Использование паттерна Команда
light = Light()
light_on_command = LightOnCommand(light)
light_off_command = LightOffCommand(light)

remote = RemoteControl()

# Включаем свет
remote.set_command(light_on_command)
remote.press_button() # Light is ON
remote.press_undo() # Light is OFF

# Выключаем свет
remote.set_command(light_off_command)
remote.press_button() # Light is OFF
remote.press_undo() # Light is ON


🚩Как это работает

🟠Интерфейс `Command`:
Объявляет методы execute и undo, которые должны реализовать конкретные команды.
🟠Конкретные команды:
Реализуют интерфейс Command и определяют, как выполнять и отменять действия.
🟠Получатель (`Light`):
Объект, который выполняет действия (в данном случае, включение и выключение света).
🟠Инициатор (`RemoteControl`):
Объект, который вызывает команды. Он не знает, что именно делает команда, а просто вызывает её методы execute и undo.

Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8