Какие существуют структурные паттерны проектирования?

Ответ

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

Основные структурные паттерны:

  1. Адаптер (Adapter)

    • Что делает: Преобразует интерфейс одного класса в другой интерфейс, ожидаемый клиентом, позволяя классам с несовместимыми интерфейсами работать вместе.
    • Когда использовать: Когда нужно интегрировать существующий класс, который имеет неподходящий интерфейс, в новую систему.
    • Пример:
      
      # Несовместимый класс
      class OldSystem:
      def old_request(self):
          return "Data from Old System"

    Ожидаемый интерфейс

    class NewSystemTarget: def request(self): pass

    Адаптер

    class Adapter(NewSystemTarget): def init(self, old_system_instance): self._old_system = old_system_instance

    def request(self):
        return f"Adapter: {self._old_system.old_request()}"

    Использование

    old_obj = OldSystem() adapter = Adapter(old_obj) print(adapter.request()) # Adapter: Data from Old System

  2. Декоратор (Decorator)

    • Что делает: Динамически добавляет объекту новую функциональность, оборачивая его в объект-декоратор. Это гибкая альтернатива наследованию для расширения функционала.
    • Когда использовать: Когда нужно добавить или удалить обязанности объекта во время выполнения, не изменяя его структуру.
    • Пример:
      
      class Coffee:
      def get_cost(self): return 5
      def get_ingredients(self): return "Coffee"

    class MilkDecorator: def init(self, coffee): self._coffee = coffee def get_cost(self): return self._coffee.get_cost() + 2 def get_ingredients(self): return self._coffee.get_ingredients() + ", Milk"

    my_coffee = Coffee() my_coffee = MilkDecorator(my_coffee) print(f"Cost: {my_coffee.get_cost()}, Ingredients: {my_coffee.get_ingredients()}")

    Cost: 7, Ingredients: Coffee, Milk

  3. Фасад (Facade)

    • Что делает: Предоставляет унифицированный (упрощенный) интерфейс к сложной подсистеме, скрывая ее сложность.
    • Когда использовать: Когда система состоит из множества взаимосвязанных классов, и нужно предоставить простой способ взаимодействия с ней.
  4. Компоновщик (Composite)

    • Что делает: Позволяет группировать объекты в древовидные структуры и работать с ними как с отдельными объектами. Клиент может единообразно обращаться как к отдельным объектам, так и к их группам.
    • Когда использовать: Когда нужно представить иерархию объектов "часть-целое" и позволить клиентам игнорировать разницу между отдельными объектами и их композициями.
  5. Заместитель (Proxy)

    • Что делает: Предоставляет суррогат или заполнитель для другого объекта, контролируя доступ к нему. Это позволяет добавить дополнительную логику (например, ленивую инициализацию, кэширование, контроль доступа) перед доступом к реальному объекту.
    • Когда использовать: Когда нужен более контролируемый или отложенный доступ к объекту.
  6. Мост (Bridge)

    • Что делает: Разделяет абстракцию и реализацию, позволяя им изменяться независимо друг от друга. Это достигается путем вынесения реализации в отдельную иерархию.
    • Когда использовать: Когда нужно избежать постоянной привязки абстракции к ее реализации или когда и абстракция, и реализация должны расширяться независимо.
  7. Приспособленец (Flyweight)

    • Что делает: Экономит память, разделяя общее состояние между множеством объектов. Используется, когда большое количество мелких объектов имеет много общего состояния.
    • Когда использовать: Когда приложение должно поддерживать большое количество объектов, и их создание или хранение становится слишком затратным по памяти.

Ответ 18+ 🔞

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

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

1. Адаптер (Adapter) Это когда у тебя есть старый, видавший виды утюг с советской вилкой, а тебе надо воткнуть его в европейскую розетку. Ты что делаешь? Берёшь переходничок, сука! Вот этот переходничок — он и есть адаптер. В коде так же: есть какой-то легаси-класс, который орет «Мууу!» как Герасим, а новая система ждёт от него человеческой речи. Адаптер берёт этого немого Герасима, оборачивает и говорит: «Всё, брат, теперь ты умеешь request() делать».

# Это наш Герасим, негостеприимный
class OldSystem:
    def old_request(self):
        return "Data from Old System"

# А это новая тёлочка-система, которая хочет только по-модному
class NewSystemTarget:
    def request(self):
        pass

# Сам адаптер-переходник. Хитрая жопа.
class Adapter(NewSystemTarget):
    def __init__(self, old_system_instance):
        self._old_system = old_system_instance # Сажаем Герасима внутрь

    def request(self):
        return f"Adapter: {self._old_system.old_request()}" # И за него говорим

# Использование
old_obj = OldSystem()
adapter = Adapter(old_obj)
print(adapter.request()) # Adapter: Data from Old System

Видишь? Герасим как мычал, так и мычит, но через адаптер он уже почти интеллигент.

2. Декоратор (Decorator) О, это любимое! Это как ты приходишь в кофейню: «Дайте мне кофе». «С молоком?» — «Да». «С сиропом?» — «А почему нет?». «С пенкой?» — «Да ебать, давайте уже всё!». Каждый раз тебе не новый кофе делают, а нахуеверчивают поверх старого новые плюшки. В коде — абсолютно так же. Берёшь базовый объект и по цепочке оборачиваешь его в обёртки, которые что-то добавляют.

class Coffee:
    def get_cost(self): return 5
    def get_ingredients(self): return "Coffee"

class MilkDecorator:
    def __init__(self, coffee):
        self._coffee = coffee # Заворачиваем кофе в молоко
    def get_cost(self): return self._coffee.get_cost() + 2
    def get_ingredients(self): return self._coffee.get_ingredients() + ", Milk"

my_coffee = Coffee()
my_coffee = MilkDecorator(my_coffee) # Оборачиваем!
print(f"Cost: {my_coffee.get_cost()}, Ingredients: {my_coffee.get_ingredients()}")
# Cost: 7, Ingredients: Coffee, Milk

И так можно обернуть хоть в десять декораторов, главное — вовремя остановиться, а то получится «Кофе с молоком, сиропом, пенкой, перцем, дошираком и чувством вины».

3. Фасад (Facade) Представь, что ты хочешь завести машину. Тебе похуй, что там: бензонасос, стартер, компьютер, свечи, поршни делают вот эту самую хуйню. Ты просто ключ поворачиваешь (или кнопку жмёшь). Эта кнопка — и есть фасад над пиздецки сложной системой двигателя. В коде фасад — это такой простой класс-привратник, который скрывает за своей спиной овердохуищу других классов, а тебе выдаёт одну-две удобные методы.

4. Компоновщик (Composite) Это паттерн для тех, у кого в голове древо, а не граф. Позволяет работать с одной штукой и с кучей этих штук — абсолютно одинаково. Ну, типа, ты можешь сказать «нарисуй» и одному кружку, и целой папке, в которой лежат кружки, квадратики и ещё три папки. Все они — «графические объекты». Удобно, когда нужно делать одно и то же действие на целую кучу всего разом. Без него пришлось бы городить адские проверки «а это у меня один элемент или мапа?».

5. Заместитель (Proxy) Заместитель — это как твой друг, который говорит: «Отдай мне посылку за тебя получу». А сам её получает, проверяет, не бомба ли, распаковывает, выкидывает счёт за ЖКХ и приносит тебе только конфетки. Контролирует доступ, короче. Ленивая загрузка? Это он. Кэширование? Тоже он. Защита доступа? Опять он, хитрая жопа.

6. Мост (Bridge) Самый, блядь, философский паттерн. Ну, смотри: есть «телевизор», а есть «пульт». Телевизор — это реализация (можно включить, громкость сделать). Пульт — это абстракция (кнопка «вкл», кнопка «громче»). Мост говорит: давайте мы их нахуй разделим, чтобы пульт от «Самсунга» мог работать и с «Сони», и с «Филипсом». И чтобы новый пульт с голосовым управлением можно было сделать, не переписывая все телевизоры в мире. Независимое развитие, епта!

7. Приспособленец (Flyweight) Это для жадных до памяти. Допустим, ты пишешь игру, и там 10 000 деревьев. Каждое дерево — объект, в нём текстура коры, листьев, модель, координаты. Текстура и модель у всех деревьев одинаковая, а координаты — разные. Так зачем 10 000 раз хранить одну и ту же текстуру? Приспособленец говорит: давайте общие данные (текстуру) вынесем в один объект-приспособленец, а в каждом дереве будем хранить только уникальные данные (координаты) и ссылку на этого приспособленца. Экономия — пиздец! Как в том анекдоте: «А халат один на всех, вот номерок».

Вот и все, блядь, структурные паттерны. Не боги горшки обжигают, а программисты, которые вовремя поняли, что если не структурировать этот хаос, то потом придётся всё ебашить с нуля, а это, простите, чих-пых тебя в сраку, никому не нужно.