Bridge и Flyweight кратко
Два структурных паттерна на закуску: Bridge борется со взрывом классов по двум осям, Flyweight экономит память.
Bridge разделяет абстракцию и её реализацию, чтобы менять их независимо. Flyweight экономит память, разделяя общее (внутреннее) состояние между множеством объектов.
Bridge: две независимые оси
Допустим, у вас есть фигуры (круг, квадрат) и способы отрисовки (вектор, растр). Если наследовать всё подряд, получится VectorCircle, RasterCircle, VectorSquare… — произведение двух осей. Bridge выносит вторую ось («как рисовать») в отдельную иерархию и связывает её композицией.
from abc import ABC, abstractmethod
class Renderer(ABC): # ось «реализация»
@abstractmethod
def draw(self, shape): ...
class VectorRenderer(Renderer):
def draw(self, shape):
return f"вектор: {shape}"
class RasterRenderer(Renderer):
def draw(self, shape):
return f"пиксели: {shape}"
class Shape: # ось «абстракция»
def __init__(self, renderer: Renderer):
self.renderer = renderer
def render(self):
return self.renderer.draw(self.name)
class Circle(Shape):
name = "круг"
print(Circle(VectorRenderer()).render())
print(Circle(RasterRenderer()).render())
Вывод:
вектор: круг пиксели: круг
Теперь фигуры и рендереры развиваются независимо: добавить Square или SvgRenderer — это плюс один класс в своей оси, а не произведение. Связь между осями — это «мост» (composition).
Flyweight: разделяем общее
Когда объектов очень много (миллионы деревьев в игре, символы в редакторе), хранить у каждого полную копию данных расточительно. Flyweight выносит общее, неизменяемое состояние (тип дерева, его текстуру) в разделяемый объект, а уникальное (координаты) держит снаружи.
class TreeType: # flyweight: общее состояние
def __init__(self, name, texture):
self.name = name
self.texture = texture
class TreeFactory:
_types = {}
@classmethod
def get(cls, name, texture):
key = (name, texture)
if key not in cls._types:
cls._types[key] = TreeType(name, texture)
return cls._types[key] # один объект на уникальный тип
# 1000 деревьев, но типов всего 2 -> экономия памяти
forest = [TreeFactory.get("дуб", "oak.png") for _ in range(1000)]
forest += [TreeFactory.get("ель", "fir.png") for _ in range(1000)]
print("деревьев:", len(forest))
print("уникальных типов:", len(TreeFactory._types))
print("один объект на тип:", forest[0] is forest[5])
Вывод:
деревьев: 2000 уникальных типов: 2 один объект на тип: True
2000 деревьев делят всего 2 объекта-типа. Координаты каждого дерева (внешнее состояние) хранились бы отдельно. Так миллионы объектов умещаются в память.
Где встречаются
Bridge — кросс-платформенная отрисовка, драйверы устройств, GUI поверх разных бэкендов. Flyweight — глифы шрифтов, тайлы в играх, кеш строк (интернирование в Python: маленькие int и некоторые строки уже разделяются).
Итог
- Bridge разделяет две независимые оси изменения, связывая их композицией.
- Flyweight разделяет общее неизменяемое состояние, экономя память.
- Оба — про управление сложностью при росте числа вариантов/объектов.