Принципы проектирования SOLID. Принцип инверсии зависимостей.

На самом деле все принципы SOLID между собой сильно связаны и основная их цель — помощь в создании качественного, способного к масштабированию, программного обеспечения. Но последний принцип SOLID на их фоне действительно выделяется. Для начала посмотрим на формулировку данного принципа. Итак, принцип инверсии зависимостей (Dependency Inversion Principle — DIP):  «Зависимость на абстракциях. Нет зависимости на что-то конкретное.». Небезызвестный специалист в области разработки ПО, Роберт Мартин, также особенно выделяет принцип DIP и представляет его просто как результат следованию другим принципам SOLID — принципу открытости/закрытости и принципу подстановки Лисков. Напомним, что первый говорит о том, что класс не должен модифицироваться для внесения новых изменений, а второй касается наследования и предполагает безопасное использование производных типов некоторого базового типа без нарушения правильности работы программы. Роберт Мартин изначально сформулировал этот принцип следующим образом:

1). Модули верхних уровней не должны зависеть от модулей нижних уровней. Модули обоих уровней должны зависеть от абстракций.

2). Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций.

То есть разрабатывать классы нужно, оперируя абстракциями, а не конкретными их реализациями. И если следовать принципам OCP и LSP, то именно этого мы и добьемся. Поэтому вернемся немного назад, к уроку, посвященному принципу открытости/закрытости. Там в качестве примера мы рассматривали класс Bard, который в самом начале был жестко привязан к классу Guitar, представляющему конкретный музыкальный инструмент:

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

Файл Instrument.java:

Файл Guitar.java:

Файл Lute.java:

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

В итоге для добавления нового функционала, нам не нужно модифицировать наш класс, а достаточно лишь реализовать интерфейс Instrument в еще одном классе. Мы много говорим об OCP. Но, последовав этому принципу, мы восстановили правильность и в отношении принципа DIP. Теперь наша абстракция — класс Bard больше не зависит от деталей (жесткой привязанности к одной конкретной реализации Guitar). Изначально мы приводили мнение Мартина о том, что принцип инверсии зависимостей не является полностью самостоятельным, а представляет собой лишь следствие из двух других принципов SOLID. Там мы упомянули принцип подстановки Лисков. В нашем примере он не нарушается, так как заменив базовый интерфейс Instrument любой из его реализаций, поведение программы не изменится. В любом случае бард заиграет на каком-нибудь музыкальном инструменте 🙂

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

dip

комментарий

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

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