php принцип инверсии зависимостей

Улучшаем архитектуру: Инверсия и внедрение зависимостей, наследование и композиция

Всем привет. Очень часто, работая со старым (а иногда и не очень) кодом, или пытаясь применить какую-то библиотеку, сталкиваешься с ограничениями в расширении. Зачастую проблемы бы не было, будь код архитектурно грамотен. Есть множество архитектурных правил и паттернов, которые в конечном счете облегчают расширение кода, рефакторинг и переиспользование. В статье хочу затронуть некоторые из них в примерах.

Давным давно в далеком далеком проекте появился сервис, отправляющий письмо с новым паролем пользователям. Примерно вот такой:

Со временем сервис обрастал наследниками, использующими частично его методы, частично новые. В один прекрасный солнечный день пришел менеджер с задачей переключиться с smtp на API популярного сервиса. Класс Mailer уже не подходит, а в коде уже целый зоопарк его упоминаний. Давайте посмотрим на этом этапе, что можно было сделать вначале, чтобы эта задача не превратилась в головную боль?

Dependency Injection (Внедрение зависимостей, DI)

Также большинство популярных реализаций позволяет подменить объект на другой для определенного сервиса, и нашему потомку не требуется уже реализовывать свой getMailer():

Казалось бы, мы решили проблему, и добавили больше гибкости. Но чтобы решить задачу с переключением, нам придется написать наследника Mailer, берущего от родителя только собственно тип (ну или, возможно, пару методов) и полностью переписать отправку. Это не есть хорошо, и вот, что мы будем делать дальше.

Принцип инверсии зависимостей (Dependency Inversion Principle, DIP)

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

и наш MailSenderInterface, соответственно, обретает вид

Но в этом случае нам придется как-то создавать объект MailMessageInterface, и в этом нам поможет фабрика

Наш сервис, соответственно, обретает такой вид

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

Наследование VS композиция

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

2. Легко покрыть тестами маленький кусок логики, а не большой класс с вызовом кучи protected/private методов

3. Легко подменить этот класс другим, если вдруг нам где-то потребуется делать что-то иначе.

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

Для начала хочу привести более понятный и очевидный пример, где композиция выигрывает. Мы имеем сервис, который дергает внешнее API, забирая какие-то данные

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

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

Согласитесь, тут гораздо больше гибкости, да и тесты написать гораздо проще.

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

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

и бывший наследник

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

Источник

Принцип инверсии зависимостей — доходчивое объяснение

Наталия Ништа, PHP Developer, в своей статье на DOU.UA привела очень неординарное пояснение принципа инверсии зависимостей. Представляем его вашему вниманию.

В этой статье я попытаюсь рассказать про принцип инверсии зависимостей (Dependency inversion principle, далее DIP). В статье будут упомянуты уровни абстракций, поэтому настоятельно рекомендую ознакомиться с этим понятием заблаговременно.

Завязка

Чтобы по-человечески разобраться в DIP, надо раскручивать историю с самого начала — с интерфейсов и принципа «проектируйте на уровне интерфейсов, а не реализаций». Не поленитесь, прочтите — это важно.

Вспоминаем, что интерфейс — это средство осуществления взаимного воздействия; общая граница двух отдельно существующих составных частей, посредством которой они обмениваются информацией (честно списала из Википедии). Короче говоря, вот у нас есть механические наручные часы. И все взрослые люди знают, как читать время, используя интерфейс «циферблат со стрелочками». Я понятия не имею, как оно устроено внутри, какие там шестерёнки-колёсики, пружинки и прочее барахло. Мне не надо знать о богатстве внутреннего мира этого чуда инженерии. Я лишь знаю, что все механические часы поддерживают интерфейс «циферблат со стрелочками», и пользуюсь этим. Происходит абстрагирование от деталей реализации.

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

Все классы надо рассматривать как абстракции, обладающие своими интерфейсами. Это и значит проектировать на уровне интерфейсов, а не реализаций. Какую именно конструкцию языка в дальнейшем мы используем, Abstract Class или Interface, — по сути также не важно.

Если желаете, можно взглянуть на этот принцип и под другим углом: нам не обязательно знать, с каким конкретным классом (реализацией) мы имеем дело (часы фирмы такой-то, модель такая-то). Достаточно знать, какой у него суперкласс, чтобы пользоваться его методами (Abstract Class или Interface, в нашем примере это циферблат со стрелочками).

Кульминация

А теперь настало время чудес: я приведу наглядный образчик проектирования с кусками кода. Так как моим основным языком программирования является PHP (простите, так вышло), то и примеры я адаптирую под особенности этого языка.

Итак, любой музыкальный инструмент производит звуки (не важно какой именно — шумит себе и всё). Конструируем:

php принцип инверсии зависимостей. Смотреть фото php принцип инверсии зависимостей. Смотреть картинку php принцип инверсии зависимостей. Картинка про php принцип инверсии зависимостей. Фото php принцип инверсии зависимостей

Например, это может быть барабан:

php принцип инверсии зависимостей. Смотреть фото php принцип инверсии зависимостей. Смотреть картинку php принцип инверсии зависимостей. Картинка про php принцип инверсии зависимостей. Фото php принцип инверсии зависимостей

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

php принцип инверсии зависимостей. Смотреть фото php принцип инверсии зависимостей. Смотреть картинку php принцип инверсии зависимостей. Картинка про php принцип инверсии зависимостей. Фото php принцип инверсии зависимостей

Вот мы и сконструировали ряд классов, акцентируясь на том, что они умеют (т.е. на интерфейсах).

А теперь давайте немного усложним наш пример и продолжим проектировать на уровне интерфейсов. Мои друзья решили сэкономить и взять, что там они выбрали, в магазине подержанных инструментов. В нём перед продажей все инструменты ремонтируются и натираются до блеска (repair), а также заворачиваются в упаковку индивидуальной формы (pack). Очевидно, что инструменты разных производителей будут по-разному чиниться и по-разному упаковываться. На одном дыхании пишем следующие классы для нашей задачи.

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

php принцип инверсии зависимостей. Смотреть фото php принцип инверсии зависимостей. Смотреть картинку php принцип инверсии зависимостей. Картинка про php принцип инверсии зависимостей. Фото php принцип инверсии зависимостей

Например вот такая губная гармошка фирмы Marys (только что придумала):

php принцип инверсии зависимостей. Смотреть фото php принцип инверсии зависимостей. Смотреть картинку php принцип инверсии зависимостей. Картинка про php принцип инверсии зависимостей. Фото php принцип инверсии зависимостей

А также нам нужен магазин подержанных инструментов, который подготавливает инструменты к продаже:

php принцип инверсии зависимостей. Смотреть фото php принцип инверсии зависимостей. Смотреть картинку php принцип инверсии зависимостей. Картинка про php принцип инверсии зависимостей. Фото php принцип инверсии зависимостей

Набор классов, мягко говоря, весёлый, но для примера нам подойдёт.

Мы не нарушали принципа «проектировать на уровне интерфейсов, а не реализаций». Мы создавали классы, концентрируясь на их способностях. Однако, давайте пристально взглянем на последний класс Pawnshop.

Допустим, по какой-то причине в будущем мы решим изменить интерфейс Instrument, в результате чего набор его методов станет другим. Или наш магазин решит вдобавок к подержанным балалайкам приторговывать ещё и абсолютно новыми инструментами, не нуждающимися ни в упаковке, ни в ремонте. Или ещё что-то произойдёт и конкретный музыкальный инструмент перестанет поддерживать знакомый нам интерфейс. Но в работе Pawnshop мы опираемся на надежду, что только что созданный конкретный объект гарантированно будет субклассом Instrument — это совершенно безрассудно. А сколько таких Pawnshop у нас по всему проекту — страшно даже представить.

Почему плохо зависеть от конкретных реализаций? Да потому, что они слишком часто меняются. А концепции (абстракции и интерфейсы) гораздо более живучи.

Развязка

Настало время взглянуть на определение принципа инверсии зависимостей, формулировок которого в ассортименте и количестве:
— Код должен зависеть от абстракций, а не от конкретных классов;
— Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций;
— Модули верхних уровней не должны зависеть от модулей нижних уровней. Оба типа модулей должны зависеть от абстракций.

Если всё так просто, то почему же этот принцип называется «инверсия зависимостей». Что инвертируется?

Вернёмся к нашим музыкальным инструментам. Хотя мы и строили классы, проектируя их на уровне интерфейсов, всё равно наш класс Pawnshop зависит от конкретных реализаций:

php принцип инверсии зависимостей. Смотреть фото php принцип инверсии зависимостей. Смотреть картинку php принцип инверсии зависимостей. Картинка про php принцип инверсии зависимостей. Фото php принцип инверсии зависимостей

Если мы попытаемся применить DIP, нам нужно будет изолировать все new внутри некоторой ограниченной области — для этого надо использовать какой-то из порождающих паттернов или Dependency Injection. В результате мы можем получить совершенно другую картину.

Например, можем сделать так:

php принцип инверсии зависимостей. Смотреть фото php принцип инверсии зависимостей. Смотреть картинку php принцип инверсии зависимостей. Картинка про php принцип инверсии зависимостей. Фото php принцип инверсии зависимостей

php принцип инверсии зависимостей. Смотреть фото php принцип инверсии зависимостей. Смотреть картинку php принцип инверсии зависимостей. Картинка про php принцип инверсии зависимостей. Фото php принцип инверсии зависимостей

Или ещё как-нибудь. Суть в том, что теперь мы получаем объекты гарантированного типа. Новая схема зависимостей будет выглядеть так:

php принцип инверсии зависимостей. Смотреть фото php принцип инверсии зависимостей. Смотреть картинку php принцип инверсии зависимостей. Картинка про php принцип инверсии зависимостей. Фото php принцип инверсии зависимостей

Данный принцип (ограничение new ) не применим к библиотечным классам. Потому что мы не будем их менять никогда. А раз эти классы «хронически неизменны», то и связанные с изменениями риски отпадают.

И вот мы вновь убедились, что ООП — это до тошноты логическая и достаточно простая для понимания вещь.

Источник

Инверсия зависимостей — PHP: Полиморфизм

Каждый раз, когда внутри функции создаётся объект, появляется зависимость функции от класса этого объекта. Другими словами функция жёстко завязана на работу в паре с конкретным классом. Есть формальный способ, позволяющий легко проверить насколько ваш код завязан в узел. Возьмите любую функцию и мысленно представьте, что вы переносите её в другой проект. Сколько за собой она потянет зависимостей (а те в свою очередь свои зависимости)? Если перенос функции потребует переноса большого количества кода, то говорят, что в коде высокая связанность.

Для развязки кода придуман даже специальный термин: Принцип Инверсии Зависимостей. Ещё он известен как DIP из SOLID. Вот его формулировка:

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

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

В докладах на тему DIP, докладчики любят, в качестве аналогии, приводить принцип Голливуда: Не надо нам звонить, мы сами вас наберём. Под этим имеется в виду, что не нужно пользоваться классами напрямую, а вместо этого получать готовые объекты как внешнюю зависимость.

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

Почти всегда, когда речь идёт про инверсию зависимостей, рядом появляется термин «инъекция зависимостей». В то время как DIP говорит о модульности, инъекция зависимостей говорит о конкретных способах её достижения. О том каким образом можно передать зависимости в код использующий их. Всего есть три способа инъектировать зависимости:

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

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

Через сеттеры. По возможности лучше этот способ не использовать. Он связан с изменением объектов и нарушением целостности (подробнее было в курсе по объектно-ориентированному дизайну).

Открыть доступ

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно.

Наши выпускники работают в компаниях:

С нуля до разработчика. Возвращаем деньги, если не удалось найти работу.

Источник

Dependency Inversion Principle

Принцип инверсии зависимостей

php принцип инверсии зависимостей. Смотреть фото php принцип инверсии зависимостей. Смотреть картинку php принцип инверсии зависимостей. Картинка про php принцип инверсии зависимостей. Фото php принцип инверсии зависимостей

D ependency Inversion Principle служит для создания слабосвязанных сущностей, которые легко тестировать, модифицировать и обновлять. Этот принцип можно сформулировать следующим образом:

Чтобы понять принцип, рассмотрим следующий пример:

php принцип инверсии зависимостей. Смотреть фото php принцип инверсии зависимостей. Смотреть картинку php принцип инверсии зависимостей. Картинка про php принцип инверсии зависимостей. Фото php принцип инверсии зависимостей

Класс Book, представляющий книгу, использует для печати класс ConsolePrinter. При подобном определении класс Book зависит от класса ConsolePrinter. Более того мы жестко определили, что печать книгу можно только на консоли с помощью класса ConsolePrinter.

Другие же варианты, например, вывод на принтер, вывод в файл или с использованием каких-то элементов графического интерфейса — все это в данном случае исключено. Абстракция печати книги не отделена от деталей класса ConsolePrinter. Все это является нарушением принципа инверсии зависимостей.

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

php принцип инверсии зависимостей. Смотреть фото php принцип инверсии зависимостей. Смотреть картинку php принцип инверсии зависимостей. Картинка про php принцип инверсии зависимостей. Фото php принцип инверсии зависимостей

Теперь абстракция печати книги отделена от конкретных реализаций.

В итоге и класс Book и класс ConsolePrinter зависят от абстракции IPrinter. Кроме того, теперь мы также можем создать дополнительные низкоуровневые реализации абстракции IPrinter и динамически применять их в программе:

Источник

Внедрение зависимостей и принцип инверсии зависимостей это НЕ ОДНО И ТО ЖЕ

Перевод статьи подготовлен в преддверии старта курса «Разработчик C++».

php принцип инверсии зависимостей. Смотреть фото php принцип инверсии зависимостей. Смотреть картинку php принцип инверсии зависимостей. Картинка про php принцип инверсии зависимостей. Фото php принцип инверсии зависимостей

Давным-давно, когда я только начинал вести блог на LosTechies, я написал несколько статей о инверсии зависимостей (Dependency Inversion — DI), внедрении (или инъекции) зависимостей (Dependency Injection — также DI) и, конечно, о том, как я наконец начал понимать, в чем суть внедрения зависимостей. В то время я полагал, что оба DI были просто вариациями названия одной техники — называли ли вы это «инъекция» или «инверсия».

Где-то через год после этих двух публикаций я написал статью для Code Magazine о принципах разработки программного обеспечения SOLID. В процессе написания я попросил моего тогдашнего сотрудника Дерека Грира побыть рецензентом этой статьи. Это оказался самый лучшим поступок, который я когда-либо совершал для моего понимания SOLID, потому что Дерек был достаточно любезен (и терпелив по отношению к моему упрямству), чтобы показать мне, где мое понимание инверсии зависимостей отличалось от правильного. Он нашел время, чтобы провести меня через оригинальные статьи Дяди Боба, объяснить, где я смешивал внедрение зависимостей с инверсией, и наконец разъяснил для меня эту тему. В результате у меня получилась отличная статья, которая до сих пор достаточно популярна — и я очень благодарен Дереку за помощь в исправлении моего понимания.

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

(Юридическая формальность: следующий текст первоначально появился в выпуске журнала CODE Magazine за январь/февраль 2010 года и воспроизводится здесь с их разрешения)

Принцип инверсии зависимостей

Принцип инверсии зависимостей (Dependency Inversion Principle — DIP) состоит из двух частей:

Тот же принцип применим и в разработке программного обеспечения. Вместо того чтобы работать с набором классов, которые жестко связаны между собой, вы бы хотели работать со стандартным интерфейсом. Более того, вы бы хотели гарантировать, что сможете заменить реализацию, не нарушая ожидания этого интерфейса, согласно LSP (Liskov Substitution Principle — Принцип подстановки Лисков). Таким образом, если вы работаете с интерфейсом и хотите иметь возможность заменять его, вам нужно следить за тем, чтобы вы работали только с интерфейсом, а не с конкретной реализацией. То есть код, который полагается на интерфейс, должен знать подробности только об этом интерфейсе. Он не должен знать ни о каких конкретных классах, которые реализуют этот интерфейс.

Взаимоотношения политик, деталей и абстракций

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

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

Уменьшение связанности путем инвертирования зависимостей

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

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

php принцип инверсии зависимостей. Смотреть фото php принцип инверсии зависимостей. Смотреть картинку php принцип инверсии зависимостей. Картинка про php принцип инверсии зависимостей. Фото php принцип инверсии зависимостей
Рисунок 14: Политика связанная с деталью.

Это реализует необходимую иерархию, но напрямую связывает классы. Вы не сможете использовать Foo, не таская за собой Bar. Если вы хотите разделить эти классы, вы можете легко ввести интерфейс, который будет реализовывать Bar, и от которого будет зависеть Foo. На рисунке 15 показан простой интерфейс IBar, который вы можете создать на основе public API класса Bar.

php принцип инверсии зависимостей. Смотреть фото php принцип инверсии зависимостей. Смотреть картинку php принцип инверсии зависимостей. Картинка про php принцип инверсии зависимостей. Фото php принцип инверсии зависимостей
Рисунок 15: Разделение с помощью абстракции.

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

Что же произойдет, когда в данном сценарии вы захотите изменить Bar? В зависимости от того, какие изменения вы хотите внести, вы можете спровоцировать цепную реакцию, которая заставит вас изменить интерфейс IBar. Foo зависит от интерфейса IBar, поэтому вы также должны изменить реализацию Foo. Возможно, вы отделили реализацию Bar, но оставили Foo зависимым от изменений в Bar. То есть Политика по-прежнему зависит от Детали.

Если вы хотите инвертировать структуру зависимостей, чтобы Деталь стала зависимой от Политики, то в первую очередь вы должны поменять свою перспективу. Разработчик, работающий с этой системой, должен понимать, что вы не только абстрагировали реализацию от интерфейса. Да, это разделение необходимо, но этого недостаточно. Вы должны понимать, кому принадлежит абстракция — Политике или Детали.

Принцип инверсии зависимостей гласит, что детали должны зависеть от политик. Это означает, что у вас должна быть политика, определяющая и владеющая абстракцией, которую реализует деталь. В сценарии Foo->IBar->Bar вам нужно рассматривать IBar как часть Foo, а не просто как обертку для Bar. Хоть структурно ничего и не поменялось, но перспектива владения изменилась, как показано на рисунке 16.

php принцип инверсии зависимостей. Смотреть фото php принцип инверсии зависимостей. Смотреть картинку php принцип инверсии зависимостей. Картинка про php принцип инверсии зависимостей. Фото php принцип инверсии зависимостей
Рисунок 16: Политика владеет абстракцией. Деталь зависит от политики.

Если Foo владеет абстракцией IBar, вы можете поместить эти две конструкции в пакет, независимый от Bar. Вы можете поместить их в свое собственное пространство имен, в свою собственную сборку и т. д. Это может значительно увеличить наглядность того, какой класс или модуль зависит от какого. Если вы видите, что AssemblyA содержит Foo и IBar, а AssemblyB обеспечивает реализацию IBar, то вам легче заметить, что детализация Bar зависит от политики, определенной Foo.

Если вы правильно инвертировали структуру зависимостей, цепная реакция на изменения политики и/или деталей теперь также будет корректной. Когда вы меняете реализацию Bar, вы больше не наблюдаете восходящей цепочки изменений. Это связано с тем, что Bar требуется соответствие абстракции, предоставленной Foo — детали больше не диктуют изменения в политике. Затем, когда вы изменяете потребности Foo, вызывая изменения в интерфейсе IBar, ваши изменения распространятся по структуре. Bar (деталь) будет вынужден меняться в зависимости от изменения политики.

Разделение и инверсия зависимостей системы отправки электронной почты

Просматривая нашу кодовую базу, вы можете заметить, что IFileFormatReader уже является примером инверсии зависимости. Класс FormatReaderService владеет определением интерфейса читателя формата. Если потребности службы чтения форматированных файлов изменятся, вы, скорее всего, увидите цепочку изменений нисходящую к читателям форматов. Однако, если изменится читатель конкретного формата файлов, вы вряд ли увидите, что эти изменения отразятся на службе чтения форматированных файлов. Это заставляет задуматься, где еще вы можете инвертировать системные зависимости.

Первое, что вы хотите сделать, это отделить логику получения логовых сообщений и отправки его по электронной почте от формы отправки. Вы не возражаете против ссылок на две службы чтения и отправки электронной почты, но вопрос когда и что вызывать порождает сомнения. Вы осознаете, что процесс фактически дублируется в форме: один раз для отправки из файла и один раз для отправки из базы данных. И затем вы вспоминаете все другие отделы, которые также это используют, и начинаете задаваться вопросом, о реальных масштабах дублирования процесса. Кроме того, некоторые из ваших друзей недавно говорили о «модульном тестировании». Они говорят, что вы должны убедиться, что реальная логика процесса, которую вы тестируете, инкапсулирована в объекты, которые не имеют ссылок на внешние системы.

И с этим всем на уме, вы решаете создать объект с именем ProcessingService. После нескольких минут тасования кода туда-сюда, чтобы попытаться консолидировать процесс, вы понимаете, что не хотите, чтобы служба обработки была связана напрямую со службами чтения базы данных или чтения файлов. После небольшого размышления вы замечаете между ними паттерн: метод «GetMessageBody». Используя этот метод в качестве основы, вы создаете новый интерфейс IMessageInfoRetriever и реализует его как службой чтения базы данных, так и службой чтения файлов.

Этот интерфейс позволяет вам предоставить любую реализацию для служб обработки, которая вам понадобится. Затем вы обращаете внимание на службу электронной почты, которая в настоящее время напрямую связана со службой обработки. Простой интерфейс IEmailService решает эту проблему. На рисунке 17 показана полученная структура.

php принцип инверсии зависимостей. Смотреть фото php принцип инверсии зависимостей. Смотреть картинку php принцип инверсии зависимостей. Картинка про php принцип инверсии зависимостей. Фото php принцип инверсии зависимостей
Рисунок 17: Инвертирование зависимостей службы обработки и службы чтения файлов.

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

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *