[PHP, Проектирование и рефакторинг, Yii] SOLID на практике. Принцип открытости-закрытости и ActiveQuery Yii2

Автор Сообщение
news_bot ®

Стаж: 6 лет 9 месяцев
Сообщений: 27286

Создавать темы news_bot ® написал(а)
03-Дек-2020 19:30

Как-то в рабочей беседе один мой коллега-программист заметил, что всевозможные принципы и паттерны проектирования ПО хорошо применять, когда делаешь тестовые задания, однако в реальных, боевых проектах они как правило неприменимы. Почему так? Основных причин две: 
  • Реализация принципов и паттернов требует слишком много времени. 
  • Код становится тяжеловесным и сложным для понимания. 
В серии статей «На практике» я попробую развеять эти предубеждения, продемонстрировав кейсы, реализующие принципы проектирования в практических задачах таким образом, чтобы этот код не был слишком сложным и на его написание уходило разумное количество времени. Вот первая статья из этой серии. Отправная точкаРаботаем над проектом на Yii2, в котором для доступа к данным используется ActiveRecord. Клиентский код загружает некий набор данных, используя метод ActiveRecord::find()
class ClientClass  

    // Много кода... 
    public function buildQuery(): ActiveQueryImplementation 
    { 
        $query = ActiveRecordModel::find(); // Получаем экземпляр ActiveQuery из модели 
        $query->active()->unfinished(); // Применяем условия, реализованные в конкретном классе ActiveQuery         
        return $query; // Далее результаты построения ActiveQuery используются для получения выборки из БД, например $query->all(); 
    } 
    // Много кода... 
Этот код применяет к экземпляру, реализовывающему  ActiveQueryInterface, фиксированный набор условий и возвращает сконфигурированный таким образом экземпляр для дальнейшего использования. А если нужно добавить новые условия в запрос?Придерживаясь традиционного подхода, мы добавим вызовы методов с новыми условиями прямо в строку, применяющую эти условия к запросу. 
$query->active()->unfinished()->newConditionA()->newConditionB(); 
Вуаля! Пять секунд работы, и мы получили изящный, легко читаемый код. Но что если эти условия нужны не во всех случаях, когда вызывается наш метод? Что если условия к запросу нужно применять динамически? Тут нас ждут определенные трудности. Очевидно, что при таком подходе весь клиентский код, использующий наш метод, будет получать запрос, к которому уже применены новые условия. Почему так получилось? Потому что мы... Нарушаем принцип открытости-закрытости Напомню, что принцип открытости-закрытости SOLID гласит: 
Код должен быть открытым для дополнения и закрытым для изменения. 
Мы изменили код нашего метода таким образом, что он начал выдавать другие результаты.  А как сделать правильно? Чтобы соблюсти принцип открытости-закрытости, нам нужно было бы предусмотреть возможность в случае необходимости дополнять код таким образом, чтобы добавлять в него новые условия, не внося изменений в логику работы метода по умолчанию и не меняя его исходный код.Для этого изменим код нашего метода следующим образом. 
class ClientClass  

    /** 
     * @var ActiveQueryFilter[] 
     */ 
    public $filters = []; // Добавляем возможность сконфигурировать экземпляр класса массивом фильтров 
    // Много кода... 
    public function buildQuery(): ActiveQueryImplementation
    { 
        $query = ActiveRecordModel::find(); 
        $query->active()->unfinished();   
        $this->applyFilters($query); // Применяем фильтры к запросу 
        return $query; 
    } 
    private function applyFilters(ActiveQueryImplementation &$query): void  
    { 
        foreach ($this->filters as $filter) { 
            $filter->applyTo($query); 
        } 
    } 
    // Много кода... 
Определим интерфейс ActiveQueryFitler предполагает единственный метод applyTo(), применяющий дополнительные условия к запросу в качестве побочного эффекта. 
interface ActiveQueryFilter 

    public function applyTo(ActiveQuery $query): void; 
Теперь для добавления в запрос новых условий нам достаточно добавить соответствующую реализацию интерфейса ActiveQueryFilter
class NewConditionsAAndBFilter implements ActiveQueryFilter 

    public function applyTo(ActiveQuery $query): void  
    { 
        $query->newCondtionA()->newConditionB(); 
    } 
Что мы имеем в итоге 
  • Мы решили стоящую перед нами задачу, дополнив первоначальный метод всего одним вызовом (минимальное вмешательство в первоначальный код). 
  • Поведение по умолчанию исходного метода не изменилось. 
  • Вызываемый из исходного метода новый метод applyFilters() не реализовывает собственной логики — вся логика делегируется классам фильтров. Таким образом, мы оставили неизменным и поведение всего исходного класса. 

===========
Источник:
habr.com
===========

Похожие новости: Теги для поиска: #_php, #_proektirovanie_i_refaktoring (Проектирование и рефакторинг), #_yii, #_solid, #_printsip_otkrytostizakrytosti (принцип открытости-закрытости), #_yii2, #_php, #_proektirovanie_i_refaktoring (
Проектирование и рефакторинг
)
, #_yii
Профиль  ЛС 
Показать сообщения:     

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

Текущее время: 22-Ноя 13:07
Часовой пояс: UTC + 5