Перейти к основному контенту
Tech Path Finder
КурсыИнтервьюКод-ревьюБлог
Tech Path Finder

Персонализированный путеводитель в IT. Квизы, мок-интервью, код ревью и аналитика прогресса.

@potapov_me

Платформа

  • Курсы
  • Прогресс
  • Мок-интервью
  • Код ревью
  • Живое ревью с ИИ
  • Тренажёр переговоров
  • Закладки

Контент

  • Блог
  • Главная
  • Обратная связь

Компания

  • О проекте
  • Тарифы
  • Условия использования
  • Конфиденциальность
  • Согласие на обработку данных
  • Cookie
  • Реквизиты

Аккаунт

  • Войти
  • Зарегистрироваться
  • Профиль

© 2026 Tech Path Finder. Все права защищены.

·ИП Потапов К.С.·Политика конфиденциальности·
Сделано с ❤️ в России
  1. Bridge и Composite
bridge_composite

Bridge и Composite

Разделение абстракции и реализации. Древовидные структуры через Composite. Применимость в Python.

Bridge и Composite

Bridge разделяет, чтобы объединить. Composite объединяет, чтобы упростить.


#Bridge: разделение абстракции и реализации

Bridge — структурный паттерн, разделяющий абстракцию и реализацию так, чтобы их можно было изменять независимо.

#Проблема: перекрёстное наследование

# ❌ ПЛОХО: Взрыв классов при комбинации class Shape: def draw(self): pass class Circle(Shape): def draw(self): print("Drawing circle") class Square(Shape): def draw(self): print("Drawing square") # Теперь нужно для каждого цвета! class RedCircle(Circle): def draw(self): print("Drawing RED circle") class BlueCircle(Circle): def draw(self): print("Drawing BLUE circle") class RedSquare(Square): def draw(self): print("Drawing RED square") # 3 формы × 5 цветов = 15 классов!

#Решение: Bridge через композицию

# ✅ ХОРОШО: Разделение через композицию from abc import ABC, abstractmethod # Реализация class Renderer(ABC): @abstractmethod def render_circle(self, radius: int): pass @abstractmethod def render_square(self, side: int): pass class VectorRenderer(Renderer): def render_circle(self, radius: int): print(f"Vector: circle r={radius}") def render_square(self, side: int): print(f"Vector: square side={side}") class RasterRenderer(Renderer): def render_circle(self, radius: int): print(f"Raster: circle r={radius}, pixels={radius*2}") def render_square(self, side: int): print(f"Raster: square side={side}, pixels={side*side}") # Абстракция class Shape(ABC): def __init__(self, renderer: Renderer): self._renderer = renderer @abstractmethod def draw(self): pass class Circle(Shape): def __init__(self, renderer: Renderer, radius: int): super().__init__(renderer) self.radius = radius def draw(self): self._renderer.render_circle(self.radius) class Square(Shape): def __init__(self, renderer: Renderer, side: int): super().__init__(renderer) self.side = side def draw(self): self._renderer.render_square(self.side) # Использование vector = VectorRenderer() raster = RasterRenderer() circle = Circle(vector, 10) circle.draw() # Vector: circle r=10 square = Square(raster, 5) square.draw() # Raster: square side=5, pixels=25 # Можно менять реализацию в runtime circle._renderer = raster circle.draw() # Raster: circle r=10, pixels=20

Преимущества:

  • 3 формы × 2 рендерера = 5 классов вместо 6
  • Добавление цвета требует только новый Renderer
  • Можно менять реализацию в runtime

#Composite: древовидные структуры

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

#Проблема: разная обработка листьев и узлов

# ❌ ПЛОХО: Клиент знает о типах объектов class File: def __init__(self, name: str, size: int): self.name = name self.size = size class Folder: def __init__(self, name: str): self.name = name self.children = [] def add(self, child): self.children.append(child) # Клиент должен проверять тип def get_size(item): if isinstance(item, File): return item.size elif isinstance(item, Folder): total = 0 for child in item.children: total += get_size(child) # Рекурсия! return total else: raise TypeError("Unknown type")

#Решение: общий интерфейс

# ✅ ХОРОШО: Единый интерфейс для всех from abc import ABC, abstractmethod from typing import List class FileSystemItem(ABC): @abstractmethod def get_size(self) -> int: pass @abstractmethod def print_structure(self, indent: int = 0): pass class File(FileSystemItem): def __init__(self, name: str, size: int): self.name = name self.size = size def get_size(self) -> int: return self.size def print_structure(self, indent: int = 0): print(" " * indent + f"📄 {self.name} ({self.size} bytes)") class Folder(FileSystemItem): def __init__(self, name: str): self.name = name self.children: List[FileSystemItem] = [] def add(self, child: FileSystemItem): self.children.append(child) def remove(self, child: FileSystemItem): self.children.remove(child) def get_size(self) -> int: return sum(child.get_size() for child in self.children) def print_structure(self, indent: int = 0): print(" " * indent + f"📁 {self.name}") for child in self.children: child.print_structure(indent + 2) # Использование — клиент работает с любым типом единообразно root = Folder("root") docs = Folder("docs") file1 = File("readme.md", 1024) file2 = File("notes.txt", 512) root.add(docs) root.add(File("config.yaml", 256)) docs.add(file1) docs.add(file2) # Единый интерфейс print(f"Total size: {root.get_size()} bytes") # 1792 bytes root.print_structure() # 📁 root # 📁 docs # 📄 readme.md (1024 bytes) # 📄 notes.txt (512 bytes) # 📄 config.yaml (256 bytes)

#Composite с операциями

from abc import ABC, abstractmethod from typing import List, Optional class Component(ABC): def __init__(self, name: str): self.name = name self.parent: Optional["Component"] = None def set_parent(self, parent: "Component"): self.parent = parent def get_parent(self) -> Optional["Component"]: return self.parent @abstractmethod def operation(self) -> str: pass class Leaf(Component): def __init__(self, name: str, value: str): super().__init__(name) self.value = value def operation(self) -> str: return f"{self.name}: {self.value}" class Composite(Component): def __init__(self, name: str): super().__init__(name) self._children: List[Component] = [] def add(self, component: Component) -> None: self._children.append(component) component.set_parent(self) def remove(self, component: Component) -> None: self._children.remove(component) component.set_parent(None) def operation(self) -> str: results = [child.operation() for child in self._children] return f"{self.name}: [{', '.join(results)}]" def is_composite(self) -> bool: return True def find(self, name: str) -> Optional[Component]: """Поиск по дереву""" if self.name == name: return self for child in self._children: if child.is_composite(): result = child.find(name) if result: return result elif child.name == name: return child return None # Использование root = Composite("root") leaf1 = Leaf("leaf1", "value1") leaf2 = Leaf("leaf2", "value2") sub_composite = Composite("sub") sub_composite.add(Leaf("sub_leaf1", "sub_value1")) root.add(leaf1) root.add(leaf2) root.add(sub_composite) print(root.operation()) # root: [leaf1: value1, leaf2: value2, sub: [sub_leaf1: sub_value1]] # Поиск found = root.find("sub_leaf1") print(found.operation()) # sub_leaf1: sub_value1

#Практическое применение

#UI Components

from abc import ABC, abstractmethod class UIComponent(ABC): @abstractmethod def render(self) -> str: pass @abstractmethod def click(self, x: int, y: int) -> bool: pass class Button(UIComponent): def __init__(self, text: str, x: int, y: int, width: int, height: int): self.text = text self.x = x self.y = y self.width = width self.height = height def render(self) -> str: return f"[Button '{self.text}' at ({self.x}, {self.y})]" def click(self, x: int, y: int) -> bool: return (self.x <= x <= self.x + self.width and self.y <= y <= self.y + self.height) class Panel(UIComponent): def __init__(self, x: int, y: int, width: int, height: int): self.x = x self.y = y self.width = width self.height = height self.children: List[UIComponent] = [] def add(self, child: UIComponent): self.children.append(child) def render(self) -> str: rendered = [f"[Panel at ({self.x}, {self.y})]"] for child in self.children: rendered.append(" " + child.render()) return "\n".join(rendered) def click(self, x: int, y: int) -> bool: if not (self.x <= x <= self.x + self.width and self.y <= y <= self.y + self.height): return False for child in self.children: if child.click(x, y): return True return False # Использование panel = Panel(0, 0, 200, 300) panel.add(Button("OK", 10, 10, 50, 30)) panel.add(Button("Cancel", 70, 10, 70, 30)) print(panel.render()) # [Panel at (0, 0)] # [Button 'OK' at (10, 10)] # [Button 'Cancel' at (70, 10)] # Обработка клика if panel.click(15, 15): print("Clicked inside panel!")

#Меню приложения

class MenuItem(ABC): @abstractmethod def get_label(self) -> str: pass @abstractmethod def execute(self): pass class ActionMenuItem(MenuItem): def __init__(self, label: str, action): self.label = label self.action = action def get_label(self) -> str: return self.label def execute(self): print(f"Executing: {self.label}") self.action() class Menu(MenuItem): def __init__(self, label: str): self.label = label self.items: List[MenuItem] = [] def add(self, item: MenuItem): self.items.append(item) def get_label(self) -> str: return self.label def execute(self): print(f"\n=== {self.label} ===") for i, item in enumerate(self.items, 1): print(f"{i}. {item.get_label()}") def select(self, index: int): if 1 <= index <= len(self.items): self.items[index - 1].execute() # Использование file_menu = Menu("File") file_menu.add(ActionMenuItem("New", lambda: print("Creating new file"))) file_menu.add(ActionMenuItem("Open", lambda: print("Opening file"))) file_menu.add(ActionMenuItem("Save", lambda: print("Saving file"))) edit_menu = Menu("Edit") edit_menu.add(ActionMenuItem("Cut", lambda: print("Cutting"))) edit_menu.add(ActionMenuItem("Copy", lambda: print("Copying"))) edit_menu.add(ActionMenuItem("Paste", lambda: print("Pasting"))) main_menu = Menu("Main") main_menu.add(file_menu) main_menu.add(edit_menu) main_menu.execute() # === Main === # 1. File # 2. Edit

#Bridge в реальном коде

#Уведомления

from abc import ABC, abstractmethod from typing import List # Реализация: каналы отправки class NotificationChannel(ABC): @abstractmethod def send(self, recipient: str, message: str): pass class EmailChannel(NotificationChannel): def send(self, recipient: str, message: str): print(f"📧 Email to {recipient}: {message[:50]}...") class SMSChannel(NotificationChannel): def send(self, recipient: str, message: str): print(f"📱 SMS to {recipient}: {message[:50]}...") class PushChannel(NotificationChannel): def send(self, recipient: str, message: str): print(f"🔔 Push to {recipient}: {message[:50]}...") # Абстракция: типы уведомлений class Notification(ABC): def __init__(self, channel: NotificationChannel): self._channel = channel self._recipients: List[str] = [] def add_recipient(self, recipient: str): self._recipients.append(recipient) @abstractmethod def get_message(self) -> str: pass def send(self): message = self.get_message() for recipient in self._recipients: self._channel.send(recipient, message) class AlertNotification(Notification): def __init__(self, channel: NotificationChannel, priority: str): super().__init__(channel) self.priority = priority def get_message(self) -> str: return f"[{self.priority.upper()}] ALERT: System warning!" class NewsletterNotification(Notification): def __init__(self, channel: NotificationChannel, issue: int): super().__init__(channel) self.issue = issue def get_message(self) -> str: return f"Newsletter #{self.issue}: Weekly updates..." # Использование email = EmailChannel() sms = SMSChannel() alert = AlertNotification(email, "high") alert.add_recipient("admin@example.com") alert.send() # 📧 Email to admin@example.com: [HIGH] ALERT... newsletter = NewsletterNotification(sms, 42) newsletter.add_recipient("+1234567890") newsletter.send() # 📱 SMS to +1234567890: Newsletter #42... # Можно легко комбинировать alert._channel = sms # Смена канала в runtime alert.send()

#Когда НЕ использовать

#Избыточность Bridge

# ❌ ПЛОХО: Bridge без необходимости class Renderer(ABC): @abstractmethod def render(self, text: str): pass class ConsoleRenderer(Renderer): def render(self, text: str): print(text) class Text(ABC): def __init__(self, renderer: Renderer): self._renderer = renderer @abstractmethod def get_text(self) -> str: pass def display(self): self._renderer.render(self.get_text()) # Зачем, если можно: def display_text(text: str): print(text)

#Избыточность Composite

# ❌ ПЛОХО: Composite для одного уровня class Component(ABC): @abstractmethod def operation(self): pass class Leaf(Component): def operation(self): pass class Composite(Component): def __init__(self): self.children = [] def operation(self): for child in self.children: child.operation() # Если нет вложенности — Composite избыточен

#Что дальше?

Изучите тему Decorator и contextlib для динамического добавления поведения.

Далее: Decorator и contextlib