Ответы пользователя по тегу Паттерны проектирования
  • Объясните как правильно применять паттерн Repository с Entity Framework?

    Fesor
    @Fesor
    Full-stack developer (Symfony, Angular)
    зачем нужно делать свой repository на каждую сущность


    Не на каждую сущность а только для сущностей, которые выступают корнями агрегатов сущностей. Ну то есть если у вас есть сущность Product и ProductImage к примеру, то репозиторий вы делаете только для продуктов.

    Почему нельзя использовать шаблон Repository для работы с несколькими сущностями сразу


    Потому что репозиторий представляет некую штуку, которая отвечает за хранение вещей. Например представьте что репозиторий это полка. У вас есть полка для одних штук, полка для других.

    Причем полки эти могут содержать различные бизнес правила вдухе "пользователь не может положить на полку больше N продуктов пока не заплатит дополнительную деньгу".

    И для каждой сущности будут свои правила как и кто может их где хранить. Банальное соблюдение принципа единой ответственности и разделения обязанностей.
    Ответ написан
  • Где осуществлять валидацию пользовательского ввода в архитектуре MVC?

    Fesor
    @Fesor
    Full-stack developer (Symfony, Angular)
    Давайте размышлять что такое валидация и зачем она нам нужна:

    Нам нужно проверять, не нарушаются ли пред-условия для действий или инварианты состояний. Например - пользователь всегда должен иметь email - такие правила можно запихивать на уровне конструктора класса пользователя. Тогда у нас не будет физически возможности "создать" пользователя не указав email. Что бы убедиться что переданная строка email - мы можем опять же завернуть "примитив" в свой тип Email, таким образов сделав подтип строк, который гарантирует нам, что любая штука относящаяся к этому типу будет являться email-ом.

    Такие правила как "у пользователя должен быть уникальный email" могут быть проверены только там, где достаточно информации. Например - некий репозиторий пользователей, или DAO. При попытке "сохранить" мы уже делаем проверку. Или мы можем делать это просто повесив ограничение на уникальность на уровне базы данных. Это уже не столь важно.

    Различие тут в том, что все это будет вызывать ошибки. То есть мы не сможем вогнать систему в "невалидное состояние" но пользователь не сможет получить список ошибок, которые он совершил вводя какие-то данные. Все что он получит - отдельные ошибки.

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

    В простых случаях, когда данные из запроса втупую мэпятся на "модель данных", мы можем проверять уже результат. Но в случае с логикой сложнее CRUD это уже не выйдет. Точнее это уже чуть сложнее. К примеру мы передали несуществующий айдишник связанной сущности. Мы запросим данные, получим нул, и валидатор выдаст нам что-то вроде "Извините, но вот эта штука обязательна к заполнению". А пользователь такой "чтааа? Я ж заполнил!" Вот если бы ему приходило сообщение мол "Выбранная вами штука не существует" - тогда ладно, но подавляющее большинство так не запаривается.

    Словом... тут нужно исходить не из "где это делается в MVC" а из "а кому это нужно и какие цели вы приследуете".

    Если что, MVC это не все приложение, это лишь способ разделить ответственность. Приложение ничего не должно знать о UI и все. То есть валидация входящих данных вполне может лежать на уровне контроллера поскольку это ему нужно сформировать список ошибок и запихнуть их к полям формы. С другой стороны, часть валидации может происходить вообще вне "MVC", где-то внутри, на уровне модели предметной области.

    Нельзя из букв "M", "V" и "C" составить слово вечность. Именно по этой причине MVC как подход для организации GUI уже лет 20 как не используется в чистом виде.
    Ответ написан
  • Правильно ли я понимаю паттерн прототип?

    Fesor
    @Fesor
    Full-stack developer (Symfony, Angular)
    А вы точно читали что это за паттерн и чем он от фабрики отличается?

    В целом ответить на вопрос можно если вы приведете пример использования данного паттерна. Ну и еще учитывайте такой момент - в вашем примере использование прототипа не рационально - клонирование таких объектов как request не сильно то и дорогое.

    p.s. метод `getClone` должен быть приватным. В остальном вроде бы все ок.
    Ответ написан
  • В чем преимущество Dependency Injection перед использованием оператора new?

    Fesor
    @Fesor
    Full-stack developer (Symfony, Angular)
    но так и не понял, в чем его прелесть.


    Не делать все руками. Описать граф зависимостей а он уже как-то сам соберется. То есть собирать граф зависимостей руками оно может даже правильнее, но с точки зрения читабельности кода, поддерживаемости кода (особенно учитывая миллионы зависимостей во фреймворках), да и просто увеличения скорости разработки, лучше использовать какой-нибудь контейнер в main и там запросить вершину графа зависимостей. А он уж разберется.

    По сути главное что бы ваш код о контейнере ничего не знал. ЧТо бы вы в один долгий зимний вечер могли влегкую заменить все на "ручную" сборку и сам код трогать не пришлось... Ну аннотации/атрибуты это уже компромис для удобства.
    Ответ написан
  • Как наследуются функций в PHP?

    Fesor
    @Fesor
    Full-stack developer (Symfony, Angular)
    функции не наследуются.

    > Возвращает baseClass, я голову сломал.

    константа эта (__CLASS__) возвращает имя класса в котором вызываемый код вы пишите. Если вам нужен тип инстанса - используйте get_class($this). Он будет возвращать именно тип инстанса с которым вы работаете.

    p.s. завязывать код на имена типов - плохая идея.
    Ответ написан
  • Как организовать работу оповещений на сервисе?

    Fesor
    @Fesor
    Full-stack developer (Symfony, Angular)
    Думаю это не очень красиво.


    1) при изменении состояния объекта кидайте ивент об этом или явно вызывайте нотификатор
    2) нотификатор добавляет в очередь задачу (beanstalkd, rabbitmq, etc)
    3) очередь ответственна за отслеживание статуса задачи
    4) воркеры берут задачи из очереди и собственно занимаются отправкой.
    Ответ написан
  • Что почитать об архитектуре приложений и фреймворков на php?

    Fesor
    @Fesor
    Full-stack developer (Symfony, Angular)
    В целом сразу вас расстрою, то что вы хотите нельзя "разобрать" за пару месяцев. На это уйдет пару тройку лет. И книги которые стоит по этой теме прочитать имеет смысл перечитывать. Ну и практика практика практика.

    Так же хочу заметить что "архитектура" это не что-то эдакое, а общее понимание команды разработчиков о том как функционирует приложение. Если этого общего понимания нет - у вас нет архитектуры. У многих разработчиков понимание пропадает уже через пару месяце стихийной разработки. Так что помимо архитектуры важно еще и процессы/методологии разработки подтянуть.

    Так же стоит заметить, что все это приследует одну единственную цель - сделать процесс разработки эффективным в краткосрочной и долгосрочной перспективе. Если скажем у вас все приложение построено на процедурах и глобальных переменных поддержка такого кода скорее всего будет сильно дороже.

    но совершенно не имею представления о правильной архитектуре приложений на php


    "Правильно" не бывает, все зависит от задачи. "Правильная" архитектура является:

    - понятной - то есть можно быстро разобраться. Это как читабельность кода (читать совершенный код Макконела) так и в принципе декомпозиция системы, разделение ответственности и т.д. (микросервисы, гексагональная архитектура). Тут можно посоветовать почитать Эрика Эванса.
    - удобной в изменениях (продумать все нереально, а сталобыть проще сделать так что бы адаптироваться под изменения в требованиях можно было эффективнее).
    - Тестируемой - то есть вы можете проверить работоспособность системы на различных уровнях. Тут стоит смотреть в сторону TDD или практик с той же идеей (сначала формулируем как работает и как мы это проверим а потом уже делаем).

    Вот и все. Далее уже есть принципы SOLID (читать Роберта Мартина), GRASP (Крэйг Ларман), GoF (лучше тут почитать head first design patterns или даже начать с Мэта Зандстры, у него помимо паттернов еще про процессы мельком рассказано).

    Ну и смысла в этом всем нет если вы еще до конца не осознали что есть инкапсуляция и полиморфизм. Многие могут рассказать что значат эти термины, но на практике эти знания они не могут применить (чаще всего страдает инкапсуляция)

    или написании чего-то своего с нуля на чистом php


    Такое решение может принимать только разработчик у которого уже есть за плечами хотя бы пяток лет опыта работы с различными (не одним) фреймворком и языками. В противном случае вы только будете заниматься бесполезным велосипедостроительством.

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


    Берете Symfony или Zend и вперед. Все остальные фреймворки "проще". То есть скрывают больше от разработчика. После них уже можно брать что угодно.

    p.s. еще прочитайте тут: www.phptherightway.com
    Ответ написан
  • Как организовать ООП-логику на JavaScipt на примере аудио-плеера?

    Fesor
    @Fesor
    Full-stack developer (Symfony, Angular)
    Вы же понимаете что это никакого отношения к ООП не имеет? Это просто объекты содержащие функции, не настоящие объекты. Ну то есть у вас нет конструкторов, у вас есть "модули".

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

    Так же используйте модули, они помогут вам убрать лишние уровни вложенности и добиться изоляции, код станет опрятнее.

    Меньше императивной логики, больше декларативного поведения. Если событий реально много можно попробовать посмотреть в сторону observable и ивент стримов.
    Ответ написан
  • MVC: где хранить и создавать представления?

    Fesor
    @Fesor
    Full-stack developer (Symfony, Angular)
    Мы сейчас про какое MVC? их много разных. Из вашего вопроса например можно сделать вывод о двух вариантах.

    Создать отдельный класс MainView, в котором будут храниться ссылки на все представления?


    Это напоминает MVC 1979 года выпуска. В этом случае контроллеры занимаются только тем что обрабатывают пользовательский ввод. А всем что касается представления занимается собственно View. Вьюшка тут у нас активна, в ней есть куча логики и бывает так что этой логики даже больше чем в модели. В нашем примере это будет тот самый MainView который будет работать напрямую с моделью и забирать нужное состояние из нее и сама себя обновлять по изменениям (или ButtonView - зависит от того чему мы хотим дать представление. Описание MVC не делает никаких ограничений сколько у вас может быть этих штуковин на скрин).

    Или хранить каждую ссылку отдельно в контроллере?


    Это больше напоминает mediating controller MVC. Этот подход был придуман с целью убрать зависимость вьюшки от модели. По сути вьюшка становится пассивной. Теперь контроллер решает когда ее обновлять и как вообще ее рендрить.

    Так что определитесь что вам нужно. Подозреваю что все же второе потому как первое никто не использует активно уже лет 20.
    Ответ написан
  • Какой смысл от синглтона, когда есть статика?

    Fesor
    @Fesor
    Full-stack developer (Symfony, Angular)
    Ведь в любом случае мы получаем глобальное состояние


    А глобальное состояние что? Правильно, плохо. У меня тут недалеко есть один небольшой проектик под iOS где ребята решили повесилиться, и сделали сингелтон с сотней публичных свойств. И вся система работает с этим глобальным состоянием плодя побочные эффекты. Инкапсуляция? не, не слышали.

    Какой смысл использовать именно singleton?


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

    В PHP, где не не особо популярна идея многопоточного программирования, и процветает "умирающая" модель выполнения, в сингелтонах вообще нет смысла. И используют их потому что... внимание... хотят иметь глобальный доступ к различной фигне, в том числе организация глобального состояния.
    Ответ написан
  • Паттерн Repository и Active Record?

    Fesor
    @Fesor
    Full-stack developer (Symfony, Angular)
    Что такое репозиторий? Это что-то что отвечает за хранение данных. Целиком и полностью. То есть вот простенький интерфейс простенького репозитория:

    interface UserRepository {
        public User getUser(UserID id);
        public void add(User user);
        public void remove(User user);
    }


    Где именно репозиторий хранит данные, в базе данных, в памяти, в файлах, на удаленном сервере с запросами через http - это все детали реализации. То есть репозиторий - это паттерн для того, что бы абстрагировать наш код от места хранения данных.

    Репозиторий который умеет только выборки делать - это не репозиторий. Это штука для выборок, Finder. Так же мы должны тут заметить, что "строками таблиц" является объект типа User, а репозиторий представляет собой абстракцию от таблицы (очень упрощенно. есть еще паттерн table data gateway который отвечает именно за одну таблицу, репозиторий же может хэндлить и связи между таблицами и вообще оперирует именно объектами предметной области а не их отображением на базу).

    Active Record же делает именно то, что говорит название. Это объект, который представляет собой одну строку из таблицы. Он сам может себя вставить, обновить или удалить из таблицы. Однако "найти себя" он не может к примеру, за это отвечает какой-то другой объект (мне нравится название Finder, поскольку это именно то что делает объект - ищет наши строки таблиц). Частенько для упрощения методы файдеров делаются как статические методы.

    По сути ActiveRecord есть ничто иное как упрощенная комбинация из Domain Object + Row Data Gateway.
    Ответ написан
  • Есть ли учебный материал по паттернам на основе пошагового создания веб-приложения?

    Fesor
    @Fesor
    Full-stack developer (Symfony, Angular)
    Бросайте паттерны. На вашем этапе обучения они пока не нужны. Изучайте принципы (SOLID, GRASP). Учитесь писать тесты, пробуйте TDD/ATDD. И тогда паттерны будут образовываться сами собой.

    Паттерны придумали не для того что бы ими проектировать, они нужны что бы называть ваши решения. Это просто словарь терминов что бы меньше времени приходилось тратить объясняя то или иное техническое решение. Но сначала код а потом паттерны, а не наоборот.
    Ответ написан
  • Модульность в mvc, как лучше реализовать?

    Fesor
    @Fesor
    Full-stack developer (Symfony, Angular)
    Создавать по модели на каждое представление?


    Модель в контексте MVC это не модель отдельных сущностей, а модель части системы (или всей системы), которая нужна для этой конкретной view. То есть представьте себе "модель" системы как нейкий объект, внутри которого есть другие объекты, которые работают с третьими и так далее. Такая вот иерархия. Так вот вьюшки выходит коннектятся каждая к своему кусочку этой иерархии, и по сути кроме этой точки соприкосновения ничего более о системе не знают. У них весьма ограниченное представление о системе и много знать им не надо.

    Так вот. В REST "вьюшка" у вас - репрезентация ресурса. И они не должны (хоть и могут) быть проекцией таблиц в базе данных.

    Получается на каждую сущность по 20 моделей?

    Тут есть несколько подходов, основной из них - не париться и просто в рамках определенного ресурса подсовывать тот кусок данных сущности который нужен. Все же не пихать в json как-то что-то проще чем пытаться достать то чего нет.

    Так же есть подход, описанный в стандарте jsonapi.org, где у вас есть дополнительные параметры в query string, которые позволяют "включать" определенные группы связанных ресурсов и т.д. Это дает нам определенную гибкость в плане работы с API но в случае например мобильных приложений есть свои нюансы.

    появилась какая-то боязнь ошибиться.

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

    Fesor
    @Fesor
    Full-stack developer (Symfony, Angular)
    зачем они вообще нужны в работе, ведь всё хорошо и без них?


    Видите ли, паттерны это не что-то такое, что "применяют намерянно", они просто появляются. Им просто дали названия и все. Это "словарь", что бы быстро сказать другому разработчику мол "мы тут медиатор впихнем, а сверху вот этой фигни фасадом закроем". И всем сразу все понятно.

    Словом, если вы разберетесь с принципами SOLID и GRASP, а так же в принципе с ООП (сокрытие состояния, обмен сообщениями между объектами разных типов), то паттерны будут появляться сами по себе.

    Еще хорошо себя показывает TDD в вопросах "как узнать как лучше делать". В виде теста описываем что должен делать объект, или как они взаимодействуют, потом делаем. Если неудобно в тестах или они становятся слишком завязаны на реализацию (опять же неудобно поддерживать) - то значит что-то пошло не так и надо рефакторить. А при наличии тестов это делать очень легко и просто.
    Ответ написан
  • Правильно ли я использую PHP-DI?

    Fesor
    @Fesor
    Full-stack developer (Symfony, Angular)
    нет, вы не правильно уловили идею. Передавать весь контейнер в качестве зависимости это нарушение этого самого паттерна и принципа инверсии зависимостей. Так можно делать только в случае циклических зависимостей либо же просто как альтернатива ленивой инициализации сервисов (хотя альтернатива так себе, а циклических зависимостей надо избегать).

    1) читаем про принцип инверсии зависимостей
    2) читаем про внедрение зависимостей
    3) если уж используете безымянные классы то они должны имплементить какой-то интерфейс или же экстендиться от какого-то класса.

    Вместо того что вы хотите должно быть:

    class Foo {
         private $db;
    
         public function __construct(Connection $connection) {
               $this->db = $connection;
         }
    
         public function makeFoo() {
              return $this->db->getRows(); // как вы и хотели
         }
    }
    
    // а это уже дергаем в конструкторе
    $foo = $di->get(Foo::class);
    $foo->makeFoo();


    то есть в контроллерах мы дожны дернуть сервис который вернет нам готовые данные.
    Ответ написан
  • Маленькая реализация ActiveRecord php?

    Fesor
    @Fesor
    Full-stack developer (Symfony, Angular)
    рекомендую перед изучением паттернов:

    1) почитать хотя бы документацию по php что бы ознакомиться с его возможностями
    2) почитать хотя бы мельком про SOLID и GRASP
    3) и вот теперь изучать паттерны.

    php.net/manual/en/language.oop5.magic.php
    Ответ написан
  • Что такое Autowiring в DI-контейнерах?

    Fesor
    @Fesor
    Full-stack developer (Symfony, Angular)
    Autowiring - автосвязывание. То есть контейнер сам на основе типов пытается догадаться от кого кто зависит. Не нужно прописывать явно все зависимости.
    Ответ написан
  • Правильно ли наследовать класс от синглтона?

    Fesor
    @Fesor
    Full-stack developer (Symfony, Angular)
    Если нет, то почему?

    Потому что в подавляющем большинстве случаев сингелтон не нужен в принципе. Избегайте использования данного шаблона проектирования по возможности.

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

    А наследоваться от класса singleton не правильно как минимум с точки зрения LSP (Liskov Substitution Principle)
    Ответ написан
  • Нормальная ли это практика инжектить сервис в энтити?

    Fesor
    @Fesor
    Full-stack developer (Symfony, Angular)
    нет, не нормальная, допускается только передача сервиса туда где он нужен через double dispatch
    Ответ написан
  • Существует ли конфигурабельный DI для golang?

    Fesor
    @Fesor
    Full-stack developer (Symfony, Angular)
    Ваша задача не решается через контейнер зависимостей - если вам надо в рантайме нужную реализацию подсовывать - это решается через какие-нибудь фабрики, которые вам предоставляет контейнер уже со всеми нужными зависимостями.
    Ответ написан