что такое inversion of control и как spring реализует этот принцип

Инверсия управления/Inversion of Control

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

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

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

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

Теперь между этими двумя программами большая разница в потоке управления — в частности, в управлении временем, когда вызываются методы process_name и process_quest. В примере с коммандной строкой я контролирую, когда эти методы вызываются, но в примере с оконным приложением нет. Вместо этого я передаю контроль оконной системе (команда Tk.mainloop). Далее она решает, когда вызвать мои методы, основываясь на связях, которые я настроил при создании формы. Управление инвертировано — управляют мной, а не я управляю фреймворком. Это явление и называется инверсией управления (также известно как Принцип Голливуда — «Не звони нам, мы сами позвоним тебе» — Hollywood Principle — «Don’t call us, we’ll call you»).

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

Ральф Джонсон и Брайан Фут.

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

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

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

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

EJB-компоненты являются хорошим примером такого стиля инверсии управления. При разработке сессионного компонента(session bean), вы можете реализовать различные методы, которые вызываются EJB-контейнером в различных точках/состояниях жизненного цикла. Например, у интерфейса SessionBean есть методы ejbRemove, ejbPassivate (сохранен во вторичное хранилище) и ejbActivate (восстановлен из пассивного состояния). Вы не можете управлять вызовом этих методов, только тем, что они делают. Контейнер вызывает нас, а не мы вызываем его.

Примечание перевода, пример:

Это сложные случаи инверсии управления, но вы столкнетесь с этим в гораздо более простых ситуациях. Шаблонный метод является хорошим примером: супер-класс определяет поток управления, субклассы наследуются от него переопределяя методы или реализуя абстрактные методы. Например, в JUnit, код фреймворка вызывает методы setUp и tearDown для вас, чтобы создавать и очищать ваш тест. Происходит вызов, ваш код реагирует — это снова инверсия управления.

Примечание перевода, пример:

В наши дни, в связи с ростом количества IoC-контейнеров, существует некоторая путаница со смыслом инверсии управления. Некоторые люди путают общий принцип с конкретными стилями инверсии управления (такими как внедрение зависимостей), которые эти контейнеры используют. Все это немного запутанно (и иронично), так как IoC-контейнеры, как правило, рассматриваются в качестве конкурента EJB, но EJB использует инверсию управления.

Этимология: Насколько я могу судить, термин инверсии управления впервые появился на свет в работе Джонсона и Фута Designing Reusable Classes, опубликованной в журнале Object-Oriented Programming в 1988 году. Работа является одной из тех, что в возрасте хороши — ее можно прочитать даже спустя пятнадцать лет. Они считают, что они взяли этот термин откуда-то еще, но не могут вспомнить откуда. Затем термин втерся в объектно-ориентированное сообщество и вновь появился в книге Gang of Four. Более красивый синоним «Принцип Голливуда», кажется, берет начало в работе Ричарда Свита в Mesa в 1983 году. В списке целей разработки он пишет: Не звони нам, мы сами позвоним тебе (Закон Голливуда): инструмент должен организовать Тахо, чтобы предупредить его, когда пользователь захочет передать какое-то событие инструменту, вместо того, чтобы принимать модель «запросить у пользователя команду и выполнить ее». Джон Влиссидес пишет колонку о C++, которая несет в себе хорошее объяснение концепции под названием «Принцип Голливуда». (Спасибо Брайану Футу и Ральфу Джонсону за помощь с этимологией).

Источник

Работа с IoC-контейнером в Spring

В этой статье рассказывается о шаблонах проектирования, на которых строится работа со Spring-контейнером и о том, каким классом он представлен во фреймворке Spring.

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

Инверсия управления (Inversion of Control) – что это

Ключевая особенность приложения, написанного на Spring, состоит в том что большую часть объектов создаем не мы, а Spring. Мы лишь конфигурируем классы (с помощью аннотаций либо в конфигурационном XML), чтобы «объяснить» фреймворку Spring, какие именно объекты он должен создать за нас, и полями каких объектов их сделать. Spring управляет созданием объектов и потому его контейнер называется IoC-контейнер. IoC расшифровывается как Inversion of Control. А объекты, которые создаются контейнером и находятся под его управлением, называются бинами.

Читайте также:  что мне делать если у меня плохое настроение

Иллюстрировать это можно так:

В общем на вход контейнер Spring принимает:

А на выходе он производит объекты – бины. То есть экземпляры классов, созданные в соответствии с конфигурацией и внедренные куда нужно (в другие бины). После этого никакие операторы new нам не понадобятся, мы будем работать в классе-бине с его полями-бинами так, будто они уже инициированы. Конечно, не со всеми полями, а только с теми, которые сконфигурированы как бины. Остальные инициализируются как обычно, в том числе с помощью оператора new.

Что такое внедрение зависимости (Dependency Injection)

Внедрение зависимости — это и есть инициализация полей бинов другими бинами (зависимостями).

Ведь помимо создания объектов, Spring-контейнер внедряет эти объекты в другие объекты, то есть делает их полями других объектов. Иногда это выглядит магически – например, контейнер способен внедрить зависимость в поле с модификатором private, для которого нет сеттера. Как же код Spring может это сделать? Дело в том, что под капотом он использует рефлексию, так что это реально. Но эти детали для нас как разработчиков не важны, главное знать, как объяснить фреймворку, какие объекты вы хотите отдать под его управление, и в какие поля других объектов вы хотите их внедрить.

Кстати, шаблон Dependency Injection не привязан к Spring, это всего лишь инициализация поля класса. Это такая обычная вещь и так часто встречается в коде (через конструктор либо сеттер), что даже странно выделять ее в отдельный шаблон. В связи со Spring это название мелькает часто наверно потому, что внедрение выполняет Spring, и у программиста тут много возни с конфигурацией зависимостей. Но с другой стороны, создание и внедрение Spring-ом — это уже другой шаблон — инверсия контроля (IoC).

Класс ApplicationContext для работы с IoC Контейнером

Конфигурация Maven

Чтобы иметь возможность работать с контейнером, добавьте в pom.xml зависимость:

Последнюю версию зависимости можно взять тут.

Класс ApplicationContext

Для работы с контейнером существует не один класс. Но удобнее всего работать с классом ApplicationContext. Чтобы инициализировать контейнер и создать в нем бины, нужно создать экземпляр класса ApplicationContext.

Как уже сказано, контейнеру для создания бинов. требуется конгфигурация, так что конструктор контейнера принимает аргумент. Существуют два подкласса ApplicationContext: ClassPathXmlApplicationContext берет конфигурацию из XML-файла, а AnnotationConfigApplicationContext – из аннотаций:

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

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

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

Мы получили бин типа Animal из контейнера в предположении, что он сконфигурирован в нашем приложении. Давайте теперь его на самом деле сконфигурируем.

Конфигурация бинов с помощью аннотаций

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

Во-первых, создадим класс конфигурации и аннотируем его с помощью @Configuration:

Во-вторых, допустим, у нас есть класс Animal, который мы хотим делать бином:

Чтобы сделать Animal бином, создадим в классе конфигурации метод,который создает и возвращает Animal, и аннотируем этот метод с помощью @Bean:

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

Возможно вы спросите, в чем смысл этого, ведь все равно мы создаем экземпляр Animal отдельным методом. Где же польза контейнера?

Да, мы прописываем метод, создающий Animal. Но мы не вызываем этот метод. Мы просто создаем контейнер. А метод вызывается контейнером во время его инициализации. И учитывая то, что бины обычно конфигурируются однотипно, выгода есть. К тому же они обычно создаются контейнером в единственном экземпляре (хотя это зависит от конфигурации) и внедряются в поля других бинов согласно конфигурации автоматически.

Причем вместо создания аннотированного @Bean метода, можно аннотировать класс Animal изнутри аннотацией @Component – а это и вовсе одна строчка.

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

Разница между аннотациями @Bean и @Component в том, что @Bean более гибкая аннотация, ею мы аннотируем метод, а не класс:

Давайте сконфигурируем второй бин Man с помощью аннотации @Component:

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

Давайте добавим в файл конфигурации одну строчку с аннотацией @ComponentScan и именем пакета для поиска бинов:

Все бины, аннотированные с помощью @ComponentScan, должны лежать в пакете «ru.javalang.ioc».

Проверим, что оба бина действительно создаются:

Мы рассмотрели, что такое IoC-контейнер, как его создать и как из него получить бины. Также мы узнали, что можно сконфигурировать бины с помощью аннотаций @Bean и @Component.

Исходый код примера для этой статьи доступен на GitHub.

Источник

Концепция Inversion of Control и основы Spring

Паттерны уменьшения зависимостей между компонентами системы

J2EE – большая и сложная спецификация, охватывающая, тем не менее, далеко не все нюансы реализации. Кроме того, многие реализации серверов приложений содержат возможности, выходящие за рамки спецификации. Разработка под конкретный сервер приложений вольно или невольно приводит к тому, что код приложения включает участки, зависимые от этого сервера. Это создает немало проблем при попытке переноса приложения под другой сервер. Spring переносим между разными серверами приложений, и поддерживает WebLogic, Tomcat, Resin, JBoss, WebSphere и другие серверы приложений.

Читайте также:  что значит во сне увидеть своего двойника

Концепция, лежащая в основе инверсии управления, часто выражается «голливудским принципом»: «Не звоните мне, я вам сам позвоню». IoC переносит ответственность за выполнение действий с кода приложения на фреймворк. В отношении конфигурирования это означает, что если в традиционных контейнерных архитектурах наподобие EJB, компонент может вызвать контейнер и спросить: «где объект Х, нужный мне для работы?», то в IoC сам контейнер выясняет, что компоненту нужен объект Х, и предоставляет его компоненту во время исполнения. Контейнер делает это, основываясь на подписях методов (таких, как свойства JavaBean) и, возможно, конфигурационных данных в формате XML.

В этой статье будут рассмотрены основные паттерны ослабления связей между компонентами системы, а также использование паттерна IoC в Sping Framework.

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

Единственное тонкое место в этом коде – это то, что LoginManager зависит от UserList. У такой зависимости можно отметить следующие недостатки:

Таким образом, из-за зависимости компонентов страдает переносимость и возможность их повторного использования.

Рассмотрим способы выхода из этой неприятной ситуации. Необходимо сделать так, чтобы класс LoginManager мог работать с любым хранилищем пользовательской базы. Для этого определим такие хранилища, как UserStorage.

public interface UserStorage <
User getUserByName ( String theUserName ) ;
>

public class UserList implements UserStorage

public class LoginManager <
private UserStorage myUserList = new UserList () ;
.
public boolean authenticateUser ( String theUserName, String thePassword ) <
User aUser = myUserList.getUserByName ( theUserName ) ;
return thePassword.equals ( aUser.getPassword ()) ;
>
.
>

Такая организация обладает гораздо большей гибкостью. Чтобы класс был действительно универсальным, достаточно добавить setUserStorage(UserStorage theStorage) в LoginManager. Любые способы хранения пользователей могут быть легко добавлены. Этот способ открывает дорогу для создания тестов отдельно для классов UserList и LoginManager. В случае с LoginManager может быть использован класс-заглушка MockUserStorage.

Рисунок 1. Начальная диаграмма классов.

Рисунок 2. Диаграмма классов с вынесением зависимости.

Итак, мы имеем прекрасные переносимые компоненты LoginManager, UserList, JdbcUserStorage, LdapUserStorage. Не стоит думать, что мы избавились от необходимости соединять их вместе. Для использования этих компонентов необходим некий класс RuntimeAssembler, который будет делать грязную работу по соединению компонентов в единую систему.

RuntimeAssembler-классы не предназначены для повторного использования или наследования от них. В больших системах эти классы максимально запутаны. IoC-контейнеры как раз и предназначены для упрощения соединения компонентов. Все они позволяют использовать API и создавать RuntimeAssember-классы, при этом XML-конфигурация системы и RuntimeAssembler не понадобятся.

Альтернативой паттерну вынесения зависимости (Dependency Injection) является паттерн Service Locator. Он широко используется в J2EE. Так, ServiceLocator может инкапсулировать все JNDI-вызовы J2EE-системы или создание (получение) UserStorage-реализации в нашем примере. Основным отличием Dependency Injection и Service Locator является то, что в Service Locator для получения реализации UserStorage используется вызов объекта ServiceLocator (см. код ниже), в то время как в Dependency Injection ассемблер создает связь автоматически.

Рисунок 3. Диаграмма для Dependency Injection.

Рисунок 4. Диаграмма для Service Locator.

Очевидно, что в паттерне ServiceLocator есть зависимость между LoginManager и ServiceLocator, в то время как LoginManager не зависит от RuntimeAssembler в Dependency Injection. Каждый может выбирать один из этих IoC-паттернов, в зависимости от своих нужд и задач. Далее будет рассмотрен IoC-контейнер, реализованный в Spring Framework.

Введение в Spring

SpringFramework Spring Framework представляет собой набор готовых решений для использования всех основных Enterpise Java технологий — JDBC, ORM, JTA, Servlets/JSP, JMX и многих других. Абстрактные классы, фабрики и бины разработаны таким образом, чтобы программисту оставалось написать только свою логику.

Spring также содержит прекрасные классы, реализующие паттерн MVC (модель-вид-контроллер) для Web-приложений. Абстрактные DAO-классы упрощают работу с persistance layer, будь то JDBC, JDO или Hibernate, и позволяют избежать больших трудностей при смене технологии. Транзакции реализуются собственным Transaction API, который позволяет использовать различные менеджеры транзакций, в том числе JTA J2EE-сервера приложений и Hibernate. Не стоит думать, что Spring является просто IoC-контейнером, он также предоставляет фундаментальную библиотеку для использования известных Java-технологий

Рисунок 5. Возможности IoC-контейнера Spring

Простой пример

Рассмотрим особенности IoC-контейнера Spring. Сам контейнер представляет собой реализацию интерфейса BeanFactory. Обычно используется реализация XmlBeanFactory, которая читает runtime-конфигурацию из XML-файла.

Этот простой фрагмент XML создает в контейнере bean с именем helloWorld, и устанавливает его свойство message в Sergei. Реализация может быть следующей:

interface HelloWorld <
void sayMessage () ;
>

public class HelloWorldImpl implements HelloWorld <
private String myMessage;

public void setMessage ( String theMessage ) <
myMessage = theMessage;
>

public void sayMessage () <
System.out.println ( «Hello world!Hello » + myMessage + «!» ) ;
>
>

public class Application <
public static void main ( String [] args ) <
BeanFactory aBeanFactory = new XmlBeanFactory ( «sample-beans.xml» ) ;
HelloWorld aHelloWorld = ( HelloWorld ) aBeanFactory
.getBean ( «helloWorld» ) ;
// выводит «Hello world!Hello Sergei!» в System.out
aHelloWorld.sayMessage () ;
>
>

Создание объектов

В приведенном примере за конструирование объекта helloWorld отвечает контейнер – атрибут class элемента bean соответствует вызову конструктора без параметров. Spring поддерживает широкий спектр механизмов создания объектов – вызов конструктора с параметрами или без, использование фабрик классов или фабричных методов. Использование всех этих методов довольно прямолинейно, единственным нюансом является то, что при использовании конструктора с параметрами необходимо указывать либо тип, либо индекс параметра конструктора (атрибуты type и index соответственно).

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

В элементе constructor-arg поддерживаются те же значения, что и в элементе property, полный их синтаксис будет рассмотрен далее.

В описаниях bean’ов могут быть заданы два режима работы – режим прототипа (используется по умолчанию) и режим синглтона (единственный экземпляр). В первом режиме каждый запрос объекта с заданным именем будет возвращать новый объект с указанными свойствами и связями, во втором режиме будет создан единственный экземпляр, и он будет возвращаться при каждом вызове к BeanFactory, независимо от контекста. Это поведение регулируется атрибутом singleton элемента bean.

Читайте также:  что такое алебастр строительный

Значение singleton по умолчанию – true.

Установка зависимостей и свойств компонентов


Жизненный цикл объектов

Контейнер Spring поддерживает методы инициализации и разрушения объектов. В любом объекте, реализующем InitializingBean, после задания значений всех декларированных свойств будет автоматически вызван метод afterPropertiesSet. На самом деле реализация этого интерфейса не обязательна, Spring предоставляет возможность вызова любого метода, указанного в атрибуте init-method в определении bean-а. Аналогичная ситуация и с методом разрушения, который вызывается при разрушении контейнера, интерфейс и атрибут называются DisposableBean и destroy-method соответственно.

Другие возможности

В этой статье кратко описаны основные возможности контейнера Spring, за более подробным описанием возможностей можно обратиться к Spring Reference Documentation, замечу лишь, что Spring активно использует не только объектно-ориентированный подход, но и аспектно-ориентированный. Аспекты и замещения методов могут быть также легко прописаны в конфигурационном файле, как и bean. Таким образом, контейнер Spring предоставляет широкий спектр опций соединения компонентов в систему, и позволяет разработчику сосредоточиться на разработке переносимых компонентов с использованием ООП и развивающейся АОП. Огромным преимуществом этого контейнера перед другими является набор классов, позволяющих использовать современные Java/J2EE технологии совместно с классами вашей предметной области.

Источник

Inversion of Control: Методы реализации с примерами на PHP

О боже, ещё один пост о Inversion of Control

Каждый более-менее опытный программист встречал в своей практике словосочетание Инверсия управления (Inversion of Control). Но зачастую не все до конца понимают, что оно значит, не говоря уже о том, как правильно это реализовать. Надеюсь, пост будет полезен тем, кто начинает знакомится с инверсией управления и несколько запутался.

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

Рассмотрим пример.
Пусть у нас есть 2 класса — OrderModel и MySQLOrderRepository. OrderModel вызывает MySQLOrderRepository для получения данных из MySQL хранилища. Очевидно, что модуль более высокого уровня (OrderModel) зависит от относительного низкоуровневого MySQLOrderRepository.

Пример плохого кода приведён ниже.

В общем и целом этот код будет отлично работать, выполнять возложенные на него обязанности. Можно было и остановиться на этом. Но вдруг у Вашего заказчика появляется гениальная идея хранить заказы не в MySQL, а в 1С. И тут Вы сталкиваетесь с проблемой — Вам приходится изменять код, который отлично работал, да и ещё и изменения вносить в каждый метод, использующий MySQLOrderRepository.
К тому же, Вы и не писали тесты для OrderModel…

И что же со всем этим делать?

1. Фабричный метод / Абстрактная фабрика

Одним из самых простых способов реализации инверсии управления является фабричный метод (может использоваться и абстрактная фабрика)
Суть его заключается в том, что вместо непосредственного инстанцирования объекта класса через new, мы предоставляем классу-клиенту некоторый интерфейс для создания объектов. Поскольку такой интерфейс при правильном дизайне всегда может быть переопределён, мы получаем определённую гибкость при использовании низкоуровневых модулей в модулях высокого уровня.

Рассмотрим выше приведённый пример с заказами.
Вместо того, чтобы напрямую инстанцировать объект класса MySQLOrderRepository, мы вызовем фабричный метод build для класса OrderRepositoryFactory, который и будет решать, какой именно экземпляр и какого класса должен быть создан.

2. Service Locator

Основная идея паттерна Service Locator заключается в том, чтобы иметь объект, который знает, как получить все сервисы, которые, возможно, потребуются. Главное отличие от фабрик в том, что Service Locator не создаёт объекты, а знает как получить тот или иной объект. Т.е. фактически уже содержит в себе инстанцированные объекты.
Объекты в Service Locator могут быть добавлены напрямую, через конфигурационный файл, да и вообще любым удобным программисту способом.

3. Dependency Injection

Setter injection

При таком методе внедрения в классе, куда внедрятся зависимость, создаётся соответствутющий set-метод, который и устанавливает данную зависимость

Constructor injection

Interface injection

Такой метод внедрения зависимостей очень похож на Setter Injection, затем исключением, что при таком методе внедрения класс, куда внедрятся зависимость, наследуется от интерфейса, который обязует класс реализовать данный set-метод.

Какие проблемы данная реализация не решает?
По правде говоря, я не вижу во внедрении зависимостей каких-то больших недостатков. Это хороший способ сделать класс гибким и максимально независимым от других классов. Возможно это ведёт к излишней абстракции, но это уже проблема конкретной реализации принципа программистом, а не самого принципа

4. IoC-контейнер

IoC-контейнер — это некий контейнер, который непосредственно занимается управлением зависимостями и их внедрениями (фактически реализует Dependency Injection)

IoC-контейнеры присутствует во многих современных PHP-фреймворках — Symfony 2, Yii 2, Laravel, даже в Joomla Framework 🙂
Главное его целью является автоматизация внедрения зарегистрированных зависимостей. Т.е. вам необходимо только лишь указать в конструкторе класса необходимый интерфейс, зарегистрировать конкретную реализацию данного интерфейса и вуаля — зависимость внедрена в Ваш класс

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

Заключение

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

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

Источник

Строительный портал