Показаны сообщения с ярлыком Path. Показать все сообщения
Показаны сообщения с ярлыком Path. Показать все сообщения

суббота, 19 июля 2025 г.

Builder, Giga, Path

Builder, Giga, Path

https://refactoring.guru/design-patterns/builder

https://refactoring.guru/ru/design-patterns/builder

https://giga.chat/link/gcsUUyXJCB

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

Строитель даёт возможность использовать один и тот же код строительства для получения разных представлений объектов

Builder is a creational design pattern that lets you construct complex objects step by step. The pattern allows you to produce different types and representations of an object using the same construction code.


Abstract Factory, Example, Giga, Path

Abstract Factory, Example, Giga, Path

https://refactoring.guru/ru/design-patterns/abstract-factory

https://giga.chat/link/gcsUUyXJCB

D:\VC25\Otus\Py\projects\des_patterns\2_abstarct_factory

Цели нашего примера:

Мы решили продемонстрировать,

как паттерн «Абстрактная фабрика» позволяет создавать целостные наборы объектов,

относящихся к определенной тематике (семейству), и гарантирует,

что объекты из одного семейства соответствуют друг другу.

Задача:

Нам нужно создать интерфейс (UI),

который поддерживает две темы оформления: светлую и тёмную.

Наша задача — спроектировать такую систему, чтобы можно было легко переключаться между темами,

при этом сохранив логичность и консистентность каждого элемента (например, кнопка, чекбокс и текстовое поле) в рамках выбранной темы.

Как мы подошли к решению:

Мы использовали паттерн «Абстрактная фабрика», потому что:

  • Нам нужно создавать целые наборы элементов интерфейса, а не единичные объекты.
  • Важно, чтобы элементы из одного набора соответствовали друг другу визуально и стилистически.
  • Должна быть возможность легко переключаться между наборами (темами), без необходимости изменять клиентский код.

Какие этапы мы выполнили:

  1. Определение абстрактных интерфейсов:
    • Мы определили абстрактный класс для UI-элементов (AbstractUIElement),
    • который обязывает все реальные элементы интерфейса (кнопки, чекбоксы,
    • текстовые поля) реализовать метод render.
    • Также мы определили абстрактную фабрику (AbstractUIFactory), которая обязана создавать полный набор элементов (кнопку,
    • чекбокс и текстовое поле).
  2. Создание конкретных реализаций:
    • Далее мы создали конкретные классы элементов интерфейса для каждой темы (светлые и темные версии).
    • Затем мы разработали конкретные фабрики (LightUIFactory и DarkUIFactory), каждая из которых способна создать набор элементов,
    • соответствующий своей теме.
  3. Организация взаимодействия:
    • Наш клиентский код (функция ui_elements_demo) принимает любую фабрику и вызывает методы фабрики для создания элементов.
    • По сути, клиентский код не знает, какую именно тему он получит — он просто просит у фабрики создать элементы и вызывает их методы.

Что получилось в результате:

  1. Модульность и расширение:
    • Весь код аккуратно разделён на части, и любая дополнительная тема (например,
    • третья — красная тема или зелёная) может быть легко добавлена путём создания новой фабрики и новых классов элементов.
  2. Независимость от конкретного типа:
    • Нет никакой зависимости между элементами одной темы и клиентским кодом.
    • Переключиться на другую тему можно просто сменив фабрику.
  3. Корректность взаимодействия:
    • Элементы из одного семейства гарантированно сочетаются друг с другом.
    • Нельзя случайно смешать светлые и тёмные элементы,
    • так как каждая фабрика создаёт однородный набор.

Итоговый вывод:

Наш пример наглядно показал,

как паттерн «Абстрактная фабрика» позволяет создавать целостные наборы объектов,

организованных по общему признаку (тема оформления).

Благодаря такому подходу наше приложение стало легко настраиваемым и расширяемым,

позволяя добавлять новые темы без изменения существующего кода.

  • Проверка правильность размещения файлов и отсутствие конфликтов имен.
  • Можно развить идею, добавив третью тему или новый тип элемента интерфейса.
  • Расширении функциональных возможностей элементов (например, добавить анимацию, интерактивность).

Abstract Factory

Abstract Factory, Giga, Path

https://refactoring.guru/ru/design-patterns/abstract-factory

https://giga.chat/link/gcsUUyXJCB

D:\VC25\Otus\Py\projects\des_patterns\2_abstarct_factory

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

1. Суть паттерна

Паттерн «Абстрактная фабрика» — это порождающий шаблон проектирования,

который предоставляет интерфейс для создания целых семейств связанных или зависящих друг от друга объектов,

не специфицируя их конкретные классы.

Другими словами,

он позволяет создавать наборы родственных объектов, придерживаясь единого подхода к созданию,

но при этом оставаясь гибким к появлению новых вариантов семейств.

Главная цель паттерна — отделение процесса создания семейства объектов от их использования,

что позволяет легко менять целый набор объектов при сохранении общей логики программы.

2. Когда использовать?

Паттерн «Абстрактная фабрика» применяют в следующих случаях:

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

Паттерн «Абстрактная фабрика» стоит использовать в следующих ситуациях:

1. Когда нужно создать семейство взаимосвязанных объектов

Часто в программах возникают ситуации, когда объекты образуют целые группы или семейства, тесно связанные друг с другом.

Например, UI-интерфейс с темой оформления (светлая тема vs. тёмная тема),

драйверы для работы с разными типами оборудования (Windows vs. Linux),

сервисы интеграции с разными поставщиками услуг (Google Cloud Storage vs. Amazon S3).

В таких случаях создание каждого объекта индивидуально привело бы к сильному

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

Паттерн «Абстрактная фабрика» позволяет свести это к минимуму,

предоставляя единую точку входа для создания всех необходимых объектов семейства.

2. Когда требуется сменить целую группу объектов без изменения клиентского кода

Одним из ключевых достоинств паттерна является возможность полного изменения семейства объектов без необходимости

переписывать существующий клиентский код.

Например, если в вашем приложении внезапно понадобится перейти с SQLite на PostgreSQL или сменить дизайн UI-интерфейса,

паттерн «Абстрактная фабрика» позволит легко это сделать,

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

3. Когда объекты связаны общим назначением, но различаются внешне

Если у вас есть группа объектов, которые выполняют сходные функции, но выглядят или

ведут себя иначе в зависимости от внешних факторов (операционной системы,

конфигурации среды, предпочтений пользователя и т.д.),

паттерн «Абстрактная фабрика» даст вам возможность группировать подобные объекты в логически обоснованные семейства.

Например, в графическом интерфейсе пользователь может видеть одинаковые элементы (полосы прокрутки, кнопки, таблицы),

но их внешний вид и поведение зависят от выбранной темы (OS X, Windows, Material Design и т.д.).

Каждая тема —

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

4. Когда появляются потенциальные комбинации семейств объектов

По мере роста проекта разнообразие комбинаций типов объектов возрастает.

Например, при работе с драйверами устройств или интеграцией с различными провайдерами облачных сервисов,

у вас может появиться потребность создать разные сочетания драйверов и провайдеров

(например, Windows с Google Cloud, Linux с AWS).

Паттерн «Абстрактная фабрика» позволяет удобно формировать эти комбинации, предоставляя удобные фабрики для каждого случая.

5. Когда приоритетна слабая связанность и высокая расширяемость

Одной из сильных сторон паттерна «Абстрактная фабрика» является слабая связанность между клиентом и конкретными классами объектов.

Клиент взаимодействует только с абстрактной фабрикой, а значит,

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

Такой подход делает систему легко расширяемой:

добавление новых семейств объектов выполняется простым добавлением новой фабрики и соответствующей реализации объектов,

не затрагивая старый код.

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

Допустим, у вас есть приложение, которое работает с несколькими видами документов (PDF, Word, Excel)

и вам нужно поддерживать печать документов в двух вариантах оформления (цветной и черно-белый).

В этом случае можно использовать абстрактную фабрику для создания семейств объектов печати:

  • Черно-белая фабрика: печатает PDF, Word и Excel в чёрно-белом режиме.
  • Цветная фабрика: печатает те же документы, но в цвете.

Таким образом, ваш клиентский код останется компактным и лаконичным,

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

Итог:

Паттерн «Абстрактная фабрика» идеален,

когда речь идет о создании целой группы взаимосвязанных объектов,

разделенных на логические семейства, и когда важен высокий уровень гибкости и легкости

в замене целых семейств объектов без существенных изменений в клиентском коде.

Factory Method, Giga, Path

Factory Method, Giga, Path

https://refactoring.guru/design-patterns/factory-method

https://giga.chat/link/gcsUUyXJCB

D:\VC25\Otus\Py\projects\des_patterns\factory_method

Паттерн «Фабричный метод»

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

1. Суть паттерна

Паттерн «Фабричный метод» — это порождающий шаблон проектирования,

предназначенный для создания объектов через косвенное обращение к фабричному методу,

который определяет интерфейс для создания экземпляров производных классов.

Такой подход позволяет наследникам самим решать, какой конкретно экземпляр создать.

Основная цель паттерна — перенос ответственности за создание объектов из

родительского класса в его подклассы, сохраняя единый интерфейс для создания объектов.

2. Проблемы, решаемые паттерном

Проблема:

допустим, у вас есть иерархия классов, и вам нужно создать объекты, принадлежащие разным классам,

исходя из контекста использования.

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

Решение:

вместо того чтобы клиентская логика напрямую создавала объекты нужного типа,

вы выносите создание объектов в отдельный метод (фабрику),

который переопределяется в подклассах.

Это предотвращает сильное сцепление клиентской логики с конкретными классами и позволяет спокойно расширять

иерархию классов без изменения клиентского кода.

3. Применение паттерна

Сценарий 1: Работа с графикой

Допустим, у вас есть приложение, работающее с фигурами (прямоугольники, окружности, линии и т.д.).

Вам нужно рисовать фигуры разного типа в зависимости от заданных параметров.

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

Решение с использованием фабрики:

Вы создаете базовый класс с фабрикой, определяющей интерфейс для создания фигуры,

а подклассы ответственны за создание определенных типов фигур.

from abc import ABC, abstractmethod

# Базовая фигура
class Shape(ABC):
    @abstractmethod
    def draw(self):
        pass

# Производные фигуры
class Rectangle(Shape):
    def draw(self):
        print("Рисование прямоугольника")

class Circle(Shape):
    def draw(self):
        print("Рисование круга")

# Базовый класс с фабричным методом
class ShapeFactory(ABC):
    @abstractmethod
    def create_shape(self):
        pass

# Подкласс, ответственный за создание прямоугольников
class RectangleFactory(ShapeFactory):
    def create_shape(self):
        return Rectangle()

# Подкласс, ответственный за создание кругов
class CircleFactory(ShapeFactory):
    def create_shape(self):
        return Circle()

# Клиентский код
def main():
    rectangle_factory = RectangleFactory()
    circle_factory = CircleFactory()

    shapes = [
        rectangle_factory.create_shape(),
        circle_factory.create_shape()
    ]

    for shape in shapes:
        shape.draw()

if __name__ == "__main__":
    main()
Результат выполнения:
Рисование прямоугольника
Рисование круга
Сценарий 2: Форматы файлов

Допустим, ваше приложение сохраняет данные в разных форматах (JSON, XML, CSV и др.)

и вам нужно уметь правильно сериализовывать объекты, основываясь на выбранном формате.

Решение с использованием фабрики:

Вы создаете базовую структуру формата с фабрикой, которая создает нужные форматы файлов,

а подклассы (форматы) отвечают за реализацию сериализации в конкретном формате.

from abc import ABC, abstractmethod

# Базовый класс формата
class Format(ABC):
    @abstractmethod
    def serialize(self, data):
        pass

# Реализации форматов
class JSONFormat(Format):
    def serialize(self, data):
        print(f"Сериализация в JSON: {data}")

class XMLFormat(Format):
    def serialize(self, data):
        print(f"Сериализация в XML: {data}")

# Фабрика форматов
class FormatFactory(ABC):
    @abstractmethod
    def create_format(self):
        pass

# Подклассы фабрик
class JSONFactory(FormatFactory):
    def create_format(self):
        return JSONFormat()

class XMLFactory(FormatFactory):
    def create_format(self):
        return XMLFormat()

# Клиентский код
def save_data(factory, data):
    format_obj = factory.create_format()
    format_obj.serialize(data)

save_data(JSONFactory(), {"key": "value"})
save_data(XMLFactory(), "<root><item>value</item></root>")
Результат выполнения:
Сериализация в JSON: {'key': 'value'}
Сериализация в XML: <root><item>value</item></root>

4. Особенности паттерна

  • Интерфейс:
  • Метод создания объекта объявлен в базовом классе, но реализация предоставляется подклассами.
  • Наследственность:
  • Важно, чтобы базовый класс знал, какого типа объекты создаются (через абстрактный метод),
  • но не имел понятия о фактическом подклассе.
  • Расширяемость:
  • Можно легко добавить новые типы объектов, создавая новые подклассы фабрики.

5. Преимущества паттерна

  • Открытая-закрытая архитектура: Новый функционал добавляется добавлением новых подклассов, не изменяя базовый класс.
  • Слабое сцепление: Низкая зависимость между клиентами и конкретными типами объектов.
  • Четкое разделение ответственности: Родительский класс отвечает за интерфейс, а подклассы — за реализацию.

6. Недостатки паттерна

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

7. Альтернативы

Альтернатива паттерну «Фабричный метод» — паттерн «Абстрактная фабрика», который создает целые семейства связанных объектов,

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


Следующим будем рассматривать следующий паттерн из списка.

То есть мы фабричным методом создаем обьект не привязанный к конкретному типу

Правильно понимаю. Зачем нам нужна отвязка от конкретного типа ?

Ответ

Верно понимаете!

Фабричный метод позволяет создавать объекты,

не привязываясь к конкретному типу, и эта особенность несет несколько важных преимуществ:

Зачем нужна отвязка от конкретного типа?

  1. Открытость для расширения:
    • Ваш клиентский код становится открытым для будущих изменений.
    • Допустим, завтра появится новая версия объекта или вообще новый тип объекта.
    • Тогда, добавив всего один новый подкласс фабрики,
    • вы получаете возможность создавать объекты нового типа без изменения самого клиентского кода.
  2. Легкость поддержки и изменения:
    • Менее связанный код проще поддерживать и развивать.
    • Вы можете сосредоточиться на изменении или добавлении конкретных видов объектов в отдельном месте (в подклассах фабрики),
    • не трогая основной код приложения.
  3. Инкапсуляция логики создания:
    • Детали создания объекта скрыты внутри фабрики. Клиентскому коду неважно, как именно создается объект,
    • и это защищает вашу программу от ненужных подробностей.
  4. Увеличение гибкости:
    • Вместо жесткой фиксации на одном конкретном типе объекта, вы даете возможность подклассам влиять на создание нужных объектов,
    • что делает код более гибким и адаптируемым к будущим потребностям.
  5. Приспособляемость к агрегациям:
    • Если объект состоит из множества мелких компонентов, фабрика может собрать объект целиком,
    • сократив усилия по ручной сборке объекта в клиентском коде.

Пример реальной пользы в Python

Допустим, у вас есть библиотека для работы с изображениями, которая должна поддерживать разные форматы (JPEG, PNG, GIF и т.д.).

Ваш код без фабрики выглядел бы примерно так:

def process_image(filename):
    extension = filename.split('.')[-1].lower()
    if extension == 'jpg':
        image = JPGImage(filename)
    elif extension == 'png':
        image = PNGImage(filename)
    else:
        raise ValueError("Unsupported image type")
    return image

Но это плохо масштабируется. Если завтра появится SVG или WebP, придется редактировать функцию process_image.

С использованием фабрики:

from abc import ABC, abstractmethod

class Image(ABC):
    @abstractmethod
    def load(self):
        pass

class JPGImage(Image):
    def load(self):
        print("Загрузили JPEG изображение")

class PNGImage(Image):
    def load(self):
        print("Загрузили PNG изображение")

class ImageFactory(ABC):
    @abstractmethod
    def create_image(self, filename):
        pass

class JPGFactory(ImageFactory):
    def create_image(self, filename):
        return JPGImage(filename)

class PNGFactory(ImageFactory):
    def create_image(self, filename):
        return PNGImage(filename)

def process_image(filename, factory):
    image = factory.create_image(filename)
    image.load()

factory_jpg = JPGFactory()
factory_png = PNGFactory()

process_image("photo.jpg", factory_jpg)  # Загрузили JPEG изображение
process_image("graph.png", factory_png)  # Загрузили PNG изображение

Теперь вы добавили слой абстракции и сделали код готовым к новому формату,

без изменения клиентского кода process_image.

Просто добавьте новый подкласс фабрики и получите нужный объект.

Итог:

Фабричный метод позволяет вашим программам оставаться открытыми для будущего развития,

гибкими и легко поддерживаемыми, а главное — отвязанными от конкретных типов объектов,

что положительно сказывается на качестве и долговечности вашего кода.