Ответы пользователя по тегу Паттерны проектирования
  • В чем сходства и отличия паттерна Builder от остальных порождающих паттернов?

    Maksclub
    @Maksclub
    maksfedorov.ru
    Builder:
    query =  (new QueryBulder())
        .select(...)
        .with(...)
        .from(...)
        .all()
        .build();

    Сложное апи создания какого-то объекта

    AbstractFactory:
    Просто создаем фабрику и просто вызываем ее метод создания

    worker = (AbstractFooFactory factory, Params params) => factory.create(params);
    
    worker(new FooMemoryFactory(), new Params())
    Ответ написан
    1 комментарий
  • Можете дать пример для использование паттерна декоратор?

    Maksclub
    @Maksclub
    maksfedorov.ru
    Пример на псевдокоде

    UserProvider userProvider = new UserProviderAggregate(
         new LoggerUserProviderDecorator(
              new CacheUserProviderDecorator(
                  new UserProvider(...)
                  new Cache(),
             ),
             new Logger(),
         ),
         new DummyUserProvider()
    )


    все классы UserProviderAggregate, LoggerUserProviderDecorator, CacheUserProviderDecorator, DummyUserProvider и UserProvider реализуют UserProviderInterface... по сути представляют из себя реализации этой абстракции

    Просто каждый передает внутренней зависимости этого же типа (UserProviderInterface) работу внутри своей реализации метода этой абстракции

    public function loadUsers(): Collection<User> {
         return new Collection<User>(...);
    }


    или cache:
    public function loadUsers(): Collection<User> {
         if(cache.has('cache_users_key')) {
            return cache.get('cache_users_key');
        }
    
        Collection<User> users = new Collection<User>(...)
    
        cache.put('cache_users_key', users)
    
        return users;
    }


    или агрегированные:
    public function loadUsers(): Collection<User> {
         if(...any condition) {
             return dbUsers.loadUsers();
         }
    
         if(...any condition) {
             return dummyUsers.loadUsers();
         }
    
         throw new Exception();
    }
    Ответ написан
    Комментировать
  • Можно ли в DTO указывать методы isName, isLimit?

    Maksclub
    @Maksclub Куратор тега PHP
    maksfedorov.ru
    Геттеры в ДТО только для типизации, так это просто структура
    Вообще с 7.4 можно юзать паблик поля
    Ответ написан
  • Что дает паттерн билдер по сравнению с обычными сеттерами?

    Maksclub
    @Maksclub
    maksfedorov.ru
    Например то, что билдер собирает не себя, а другой объект/ы опираясь на сконструированные детали

    А сеттеры просто мутируют объект, по сути структура голая с паблик свойствами

    Один паттерн, другой структура — вот и разница
    Ответ написан
  • В чем отличие схемы, модели и сущности?

    Maksclub
    @Maksclub
    maksfedorov.ru
    Модель — некоторая абстракция того, что мы проектируем (моделируем):
    - или модель бизнес-абстракции, будь то сущности (платёж, товар, заказ) с состоянием или будь то бизнес-процессы (заказ товара, списание баланса за покупку, увольнение) без состояния или из совокупность
    - или модель данных/хранения (например изображений)
    - или модель чего угодно, что мы спроектировали, кроме инфраструктурных деталей и связующих с ними деталей (контроллеров к примеру), если это не базовая суть нашего кода

    Сущность — бизнес-модель, которая имеет явный идентификатор и своё состояние (конкретный документ, конкретный юзер, конкретный платёж, где конкретность обусловлена некоторым уникальным идентификатором)

    Схема — абстрактный план чего-то (связей или процессов к примеру)
    Ответ написан
    Комментировать
  • Закон Деметры и Doctrine?

    Maksclub
    @Maksclub Куратор тега PHP
    maksfedorov.ru
    то как быть с ситуацией, когда в некоторых модулях должны производиться сложные выборки с фильтрами, агрегацией, затрагивающие сущности (entity) других модулей?


    Для решения задач в другом модуле/домене нет необходимости иметь сущности др проекта/модуля/бандла. Пример в модуле доставки
    • не нужно знать ВСЕ о товаре и логике его сбора, нужно знать размеры и название
    • в этом модуле не нужно знать и о заказе все — только адрес, время. История его обработки и статусы (все это есть в агрегате/сущности Заказ), влияние акций и промо — это знать не нужно, но оно есть!... передавая сущность куда-то вы передаете знания и связываете разные модули
    • не нужно знать о пользователе много, кроме телефона, имени и адреса


    Потому вы можете из БД выбирать только необходимые данные конкретному модулю. Этакие проекции по потребностям. Это есть соблюдение закона Деметры — вы не лезете в другой модуль, вы лезете в инфраструктуру (вселенную), которая даст вам ровно те знания и состояние, которые нужны . То есть за своими данными.. Более того эти знания могут собираться различными способами и не обязательно из реляций доставаться, а могут быть агрегированные в NoSQL виде к примеру.

    Крч для одного модуля вы получаете некоторые проекции/данные, которые в этом модуле вы даже не скажете — откуда они пришли, тк нет этих знаний — из персистенс-слоя и все...
    Ответ написан
    Комментировать
  • Каким паттерном можно заменить трейт?

    Maksclub
    @Maksclub Куратор тега PHP
    maksfedorov.ru
    По вопросу: Есть такой паттерн, как "поведения", аналог mixin/trait — он не обозначен как паттерн в книгах, тк в книгах его нет.
    Эта реализация есть в фреймворке Yii2. Краткий обзор паттерна:
    https://rmcreative.ru/blog/post/mixin-v-php

    Но трейты лучше :)

    Если учесть, что вы хотите избавиться от наследования, то это не совсем к паттернам, а к принципам и архитектуре, к построению абстракций. А паттерны будут как следствие
    Ответ написан
  • Цепочка обязанностей и Декораторы, в чем передавать данные?

    Maksclub
    @Maksclub
    maksfedorov.ru
    при анализе код увидел, что постоянно лазию в бд за одной записью (за одной и той же), чего, конечно же хотелось бы избежать. посоветуйте пожалуйста, как передавать данные между объектами?
    Есть такой подход как UnitOfWork, в котором складываются объекта из персистенс слоя (слоя хранения), соответственно когда идет повторное обращение — запрос в БД не идет...

    использую АR.
    А вот тут проблема, тк AR модуль — кусок работы БД, а не отделенная от нее штука. То есть она (модель) ходит в хранилище, а не за ней куда-то и кто-то ходите... Ярчайший пример, когда нарушение SRP очень больно сказывается.

    Решение: можно навернуть сверху модельки некоторый провайдер, который будет персистить загруженные объекты, и по программе путешествовать именно не модель, а провайдер... Тогда можно будет доставать из него имеющий объект, а если нет его — загружать
    Ответ написан
    Комментировать
  • Когда применяем паттерн Стратегия, а когда Декоратор?

    Maksclub
    @Maksclub
    maksfedorov.ru
    Стратегия = полиморфизм, то есть мы завязаны на некий интерфейс, а какая реализация — нам не важно. Это история про зависимости. Ну например почтальон отдает пенсию бабушкам (любым, какой бабушке именно — зависит от стратегии, КОТОРАЯ НЕ СВЯЗАНА с модификацией конкретной бабушки:)

    Декоратор, это про добавить функционал в рамках одного интерфейса, тут вообще не рассматривается вопрос каких-либо отношений (к примеру бабушки и почтальона), тут рассматривается — бабушка в шубе или бабушка с загаром или бабушка на коляске, все та же бабушка, но "обернутая" неким поведением :) Главное что бабушка всегда остается быть той самой для всех бабушкой. То есть это не противопоставление — ни в начале ни в середине мы не завязываемся на дополнительное поведение бабушки у почтальона. Бабушка и все, а какая именно — зависит от стратегии разноса (например по названию улицы). Если выйдет к нему "декорированная" бабушка-качок — пенсию он даст ей также, как и не качку, тк она для него всего лишь некий субъект/абстракция, главное чтобы возраст и ФИО сошлись.

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

    Кое-где не корректные аналогии, и в аналогии стратегия есть бабушка, но в целом для понимания норм и не критично :)
    Ответ написан
    Комментировать
  • Является ли это (делегирование) нарушением принципа единственной ответственности – SRP?

    Maksclub
    @Maksclub
    maksfedorov.ru
    Если в объекте Order будет зависимость ProductList, то не нарушает SRP, тк этот объект (судя по названию) VO и состояние этого объекта необходимо для работы Order.

    Но вызывает вопросы способ работы с ним:
    создавать отдельный метод getProductList, в котором будет инициализирован класс

    Почему не в конструкторе объекта Order? Или почему не из зависимости извне (DI) вообще получать товары?
    class Order
    {
         private ProductStorageInterface $products;
    
         public function __construct(ProductStorageInterface $products) 
         {
               $this->products = $products;
         }
         
         public function refundProduct(Product $product): void
         {
               // логика возврата товара 
               // и соответственно изменение  состава $this->products
         }
    }


    И товары могут подгрузиться из памяти, БД или АПИ внешней.
    Тут заодно и принцип инверсии зависимости

    UPD: Не называйте метод getProductList(), это ужасно! Называйте как есть: createProductList() или loadProductList(), ну в зависимости от логики.
    get -- слишком общее слово и совсем не говорит о том, что происходит. Если все кругом будет сервисами (service) с методами get, то вам весело будет жить...
    Ответ написан
    4 комментария
  • Зачем нужна абстракция в ООП?

    Maksclub
    @Maksclub
    maksfedorov.ru
    В Python нет интерфейсов, потому приходится так объявлять абстракцию без поведения.
    Зачем нужна абстракция -- затем, чтобы код не завязывать на конкретный объект, а завязать на некоторые границы поведения (что и есть абстракция) -- в данном случае на некоторый SUbject
    class Observer(ABC):
        @abstractmethod
        def update(self, subject: Subject) -> None:
            """
            Получить обновление от субъекта.
            """
            pass

    Теперь Observer (все его потомки) в параметрах метода update() принимают ЛЮБОЙ объекта типа Subject, то есть код завязан на абстракцию, а не реализацию этих subjects и вообще ничего не знает о их названиях и индивидуальном поведении, знает только, что прилетит любая абстракция типа Subject....
    Ответ написан
    Комментировать
  • Какой паттерн использовать для задачи получение заказов, отправка статусов заказов из нескольких разных внешних источников?

    Maksclub
    @Maksclub Куратор тега PHP
    maksfedorov.ru
    Начинаем с границ
    Очерчиваем границы нашей системы, как-будто у нас есть этот "идеальный заказ".
    Это будут интерфейсы некоторых абстрактных реквестов и респонсов.
    Реквест на обновление, добавление и респонс на получение и т.д.... И несколько ДТО их реализующие или которые и будут этим интерфейсом сами по себе.

    Далее вы строите клиент OrderClientInterface, который выше созданные реквесты отправляет, респонсы возвращает. И на его интерфейс вы завязываетесь. Строите поверх него фабрику, которая построит вам нужный клиент под нужную систему :)

    Адаптеры (к слову это и паттерн одноименный)
    Клиенты-адаптеры уже будут связывать АПИ внешних систем с интерфейсом вашего абстрактного (имеется в виду интерфейс) клиента. Через guzzle, или через некий свой sdk, уже для вашего домена не важно, дело техники. С авторизацией или без нее... Это детали клиента.

    Это довольно общая рекомендация, но стоящая и очень упростит вам работу. Несколько интерфейсов, несколько дто, фабрика и остальное дело техники, просто и надежно

    Бизнес-логика
    Теперь пишите хэндлеры в вашей бизнес-логикек: из контроллера, демона, команды вызываете нужные хэндлеры и строите запросы. Саму обработку их результата и контроль состояния делаете в сущностях.
    Ну я так понимаю, вопрос касался адаптеров.
    Ответ написан
    Комментировать
  • Модели в паттернах MVC/MVP?

    Maksclub
    @Maksclub
    maksfedorov.ru
    Но что если мне необходимо вывести список всех работников,

    Вам поможет такой паттерн, как Repository
    Для каждой модели создается некий класс, который инкапсулирует в себя детали как и с помощью чего он это делает (через SQL или из памяти, через ORM или без) и отдает всего лишь коллекции/массивы нужных вам готовых обьектов)

    Примерно как-то так:
    class WorkerRepository(object):
         def __init__(self, DBSession)
    
         def get_dismissed_count(self, count)
    
         def get_all(self)


    Пример: jordifierro.com/django-clean-architecture
    Тут примеры на Джанго, но на самом деле архитектура приведена из книги Роберта Мартина и относится и архитектуре десктоп-приложений, особенно присмотритесь к тому, как там организованы репозитории
    Ответ написан
    4 комментария
  • Какую архитектуру выбрать для системы с задачами, которые требуют множественной обработки/подтверждения?

    Maksclub
    @Maksclub
    maksfedorov.ru
    State Machine

    Хорошо себя показывает в разного рода CRM, колл-центрах, Order Proceesing в сложных системах.
    Из одного состояния можно перейти только в определенные, каждое состояние валидно, открыты только определенные переходы и в целом такой подход к проектированию помогает бороться со сложностью условий и контролем состояний.

    Пример схемы 1
    5d150bd349906770079074.png
    Пример схемы 2
    5d150cbb9ab7f176798816.png
    Ответ написан
    3 комментария
  • Design Patterns для javascript-разработчика?

    Maksclub
    @Maksclub
    maksfedorov.ru
    Ответ написан
    Комментировать
  • Какой использовать паттерн проектирования для интеграции c внешним сервисом?

    Maksclub
    @Maksclub Куратор тега PHP
    maksfedorov.ru
    Не складывается все пока в единую структуру

    Глаза боятся, руки делают

    Во внешнем сервисе есть авторизация, добавление/изменение n-го количества сущностей (пользователь, заказ и т.д.). Основная задача паттерна, обойтись малой кровью при замене одного внешнего сервиса на другой, когда потребуется ее заменить. Или возможность переключаться между несколькими внешними системами.

    Допустим вы под таким сервисом понимаете сервис доставки -- такой сервис в будущем понадобится подменить, заменить и т.д... Там есть и работа с пользователями и с заказами.

    Простой набросок со Стратегией

    Можно придумать некий интерфейс клиента:
    // Принимает ваши учетные данные
    DeliveryClientInterface::__construct(?string $account = null, ?string $password = null)
    
    // Регистрируем покупателя в сервисе
    DeliveryClientInterface::registerCustomer(DeliveryCustomer $customer): int
    
    // Получаем заказы покупателя в сервисе
    DeliveryClientInterface::getOrders(int $customerId): DeliveryOrder
    
    // Добавляем заказ покупателю
    DeliveryClientInterface::addOrder(int $customerId, DeliveryOrder $order): int
    
    // Оповещение покупателя в сервисе о неком действии, связанной с ним в этом сервисе
    DeliveryClientInterface::notifyCustomer(DeliveryEvent $event): bool


    И научить свой проект работать с таким кодом, через интерфейсы
    // что в конструктор сервиса запихнете, например PochtaClient или PickPointClient,
    // с тем и будете работать
    class DeliveryService
    {
        public function __construct(DeliveryClientInterface $deliveryClient, User $user)
    }
    
    $userOrders = $this->deliveryService->getOrders($user->getUuid());


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

    Далее в клиентском коде подключайте нужный вам клиент через конфиг/контейнер и все будет работать.
    Ответ написан
    Комментировать
  • Нужна подсказка в объяснение паттерна?

    Maksclub
    @Maksclub
    maksfedorov.ru
    нет, не так,
    прокси -- переадресация по сути

    Обращаемся например к Shop::getDeliveryVariants();
    а этод метод сам ничего не делает,
    кроме как проксирования на Delivery::getDeliveryVariants()

    А паттерн устроен так -- запрашиваем методы у какого-то класса, и если таких нет у него ( или всегда) -- он запрашивает методы у других классов и отдает как свои
    Почти как Декоратор, но глупый, просто проксирует (переадресует) другому

    Советую рассматривать этот паттерн в связке:
    Декоратор, Прокси, Адаптер -- они очень схожи и проще будет понять
    Ответ написан
    2 комментария
  • Что такое и какая разница между паттернами и алгоритмами?

    Maksclub
    @Maksclub
    maksfedorov.ru
    Лучший курс по алгоритмам, который я встречал (и сейчас сам изучаю)
    МФТИ: Алгоритмы и структуры данных на Python 3

    Пусть вас не смущает Python -- он очень логичный и простой, и шикарный
    Ответ написан
    Комментировать
  • Как управлять создаваемым типом объекта в методе другого класса?

    Maksclub
    @Maksclub
    maksfedorov.ru
    Паттерн Стратегия

    Делаем общий для объектов Event интерфейс (с головы придумал метод begin() ):
    interface EventInterface
    {
            public function begin();
    }


    и создаем разные events:
    class ConcreteEvent implements EventInterface
    {
            public function begin()
            {
                    // ..
            }
    }
    class OtherEvent implements EventInterface
    {
            public function begin()
            {
                    // ..
            }
    }


    В нашем MyClass создаем свойство $event и для него сеттер и используем его:
    Class MyClass
    {
            private $event;
    
            public function __construct(SomeClass $obj, AnotherClass $obj2)
            {
                    // инициализация с переданными параметрами
            }
            
            public function setEvent(EventInterface $event)
            {
                    $this->event = $event;
            }
    
            private function doSomething()
            {
                    // Работаем с нашим event
                    $someDoing = $this->event->begin();
            }
    }


    Тогда уже в конечном коде нам нужно будет работать так примерно:
    $myObj = new MyClass(SomeClass $obj, AnotherClass $obj2);
    
    // Set Event
    $myObj->setEvent(new OtherEvent);
    
    // Далее работаем с наши готовым объектом, у которого уже есть нужны Event


    ЗАРАНЕЕ ПРИНОШУ ПРОЩЕНИЯ, ЕСЛИ ОШИБСЯ — сам как 3 дня изучаю паттерны
    Возможно вам не подойдет применение далее по реализации — нужно смотреть что и как

    Почему-то думал, что такой вопрос возымеет популярность
    Ответ написан