Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ТП шпоры.docx
Скачиваний:
10
Добавлен:
20.09.2019
Размер:
115.03 Кб
Скачать

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

Модули высокого уровня не должны зависеть от модулей низкого уровня. Оба типа модулей обязаны зависеть от абстракций. Абстракции не должны зависеть от подробностей. Подробностям следует зависеть от абстракций.

Рассмотрим зависимость модулей высокого уровня от низкоуровневых моду­лей. Именно модули высокого уровня включают важные политические решения и бизнес-модели приложения. Они описывают суть приложения. Но до тех пор, пока эти модули зависят от модулей низкого уровня, изменения последних могут непосредственно влиять на функционирование модулей более высокого уровня. В итоге формируется пресловутый "замкнутый круг"! Существуют модули высокого уровня, устанавливающие определенные политики, но на них влияют в целом детали модули низкого уровня. Модули, содержащие описания бизнес-правил высокого уровня, должны иметь определенную независимость и приоритет перед модулями, включающими детали. Модули высокого уровня ни в коей мере не должны зависеть от модулей низкого уровня. Более того, к высокоуровневым модулям, определяющим основные положе­ния политик, приходится обращаться повторно. Модули низкого уровня достаточ­но удобно использовать повторно в виде библиотек подпрограмм. Если модули высокого уровня зависят от модулей низкого уровня, довольно сложно использо­вать высокоуровневые модули в различных контекстах. Но если модули высокого уровня не зависят от модулей низкого уровня, высокоуровневые модули легко применять повторно. Этот принцип положен в основу схематического проектиро­вания (framework design).

Слой Policy

Слой Mechanism

V

Слой Utility

На этой диаграмме слой высокого уровня Policy использует слой низкого уровня Mechanism, который в свою очередь задействует слой на уровне деталей Utility. С сожалением приходится констатировать, что слой Policy сильно реагирует на любые изменения слоя Utility. Свойство зависимости обладает транзитивностью. Слой Policy зависит от некоторых понятий, относящихся к слою Utility; поэтому слой Policy транзитивно зависит от слоя Utility. На рис..2 показана более совершенная модель. Каждый слой верхнего уров­ня объявляет абстрактный интерфейс для необходимых служб. Затем на основе этих абстрактных интерфейсов реализуются слои нижних уровней. Каждый класс более высокого уровня с помощью абстрактного интерфейса использует следу­ющий нижайший уровень. Таким образом, слои верхнего уровня не зависят от слоев низкого уровня. Вместо этого слои, расположенные ниже, зависят от аб­страктных служебных интерфейсов, объявленных в верхних слоях. Нарушается не только транзитивная зависимость PolicyLayer от UtilityLayer, но также и непосредственная зависимость PolicyLayer от MechanismLayer. Policy

«interface»

Policy Service

Interface -

Слой Policy

Mechanism

«interface»

Mechanism

Service

Interface

Слой Mechanism

й

|

Utility

i

1

Слой Utility

Обратите внимание, что инверсия затрагивает не только отношения зависимо­сти, но также и отношения собственности для интерфейсов. Можно представлять, что библиотеки утилит содержат собственные интерфейсы. Применяя принцип DIP, заметим, что клиенты располагают абстрактными интерфейсами, а серверы просто их используют. Используя инверсию собственности, PolicyLayer не затрагивается изме­нениями на слоях MechanismLayer или UtilityLayer. Более того, PolicyLayer может повторно применяться в любом контексте, определяющем модули нижних уровней в соответствии с PolicyServicelnterface. Таким образом, при инвертировании зависимостей получаем более гибкую, надежную и переносимую структуру. В упрощенной форме суть принципа DIP можно интерпретировать с помо­щью эвристического утверждения о "зависимости от абстракций". Проще говоря, рекомендуется не использовать зависимость от статичного класса — все взаимо­отношения в программе поддерживаются с помощью абстрактного класса или интерфейса. В соответствии с этим утверждением получим ряд выводов: -ни одна переменная не должна содержать указатель или ссылку на статич­ный класс; -ни один класс не должен порождаться статичным классом. -ни один метод не должен отвергать метода реализации любого из базовых классов.

Инверсия зависимостей проявляется всякий раз, когда один класс направляет сообщение другому. Например, рассмотрим взаимоотношения объектов Button и Lamp. Объект Button воспринимает воздействие со стороны внешней среды. После получения сообщения Poll этот объект определяет, "нажал" ли соответствую­щую кнопку пользователь. Не имеет значения механизм связи с внешней средой. Пиктограмма кнопки может относиться к графическому интерфейсу пользователя, воздействие на реальную кнопку осуществляется путем нажима пользователем, а движение объекта, связанного с кнопкой, — отслеживаться домашней системой безопасности. Объект Button уточняет, активизировал ли пользователь в данный момент кнопку.

Button

+ TurnOn() + TurnOff()

+ PolK)

На объект Lamp оказывает влияние внешняя среда. После получения сообще­ния TurnOn зажигается свет. При получении сообщения Turnof f — свет гаснет. При этом не важен физический механизм произведенных действий. Каким образом сконструировать систему, в которой объект Button управляет объектом Lamp? На рис. приводится соответствующая примитивная схема. Объект Button получает сообщения Poll, уточняет, не нажата ли кнопка, а затем просто пересылает объекту Lamp сообщение TurnOn или Turnof. В чем же состоит примитивизм подобной схемы? Обратимся к коду Java, ис­пользующему эту модель. Обратите внимание, что класс Button непосредственно зависит от класса Lamp. Вследствие этой зависимости на класс Button оказывают влияние изменения в классе Lamp. Более того, отсутству­ет возможность повторного использования Button для контроля над объектом Motor. При данной схеме разработки объекты Button контролируют объекты Lamp и только объекты Lamp.

public class Button { private Lamp itsLamp; public void poll() {

if (/*условие*/) itsLamp.turnOn();} Приведенное решение нарушает основополагающие условия, накладываемые принципом DIP. Высокоуровневая политика приложения не отделена от реализа­ции низкого уровня. Абстракции не отделены от деталей. Без подобного отделе­ния политика высокого уровня автоматически попадает в зависимость от модулей низких уровней, а абстракции — от деталей.

18.Принцип отделения интерфейсов.

Этот принцип позволяет преодолевать недостатки, связанные с чрезмерной "толщиной" интерфейсов. Классы, имеющие "тучные" интерфейсы, недостаточно компактны. Эти интерфейсы можно разбить на группы методов, и каждая группа обслуживает иной набор клиентов. Поэтому каждая группа клиентов использует определенную группу членов-функций. Принцип ISP ориентируется на наличие объектов, нуждающихся в несвязан­ных интерфейсах; также учитывается, что клиенты не должны представлять их как отдельный класс. Вместо этого клиентам следует иметь представление об абстрактных базовых классах, имеющих связанные интерфейсы. Рассмотрим систему обеспечения безопасности. В этой системе имеются объ­екты Door, которые могут быть как блокированы, так и открыты. Security Door class Door

{public: virtual void Lock() = 0; virtual void Unlock() = 0; virtual bool IsDoorOpent) = 0;};

Данный класс абстрактен, поэтому клиенты могут применять объекты, от­носящиеся к интерфейсу Door, вне зависимости от определенных реализация Door. Рассмотрим одну из таких реализаций, TimedDoor, которая подает звуковой сигнал, если дверь открыта слишком долго. Чтобы выполнить эти действия, объект TimedDoor связывается с другим объектом под названием Timer.

class Timer {public:

void Register(int timeout, TimerClient* clients);};

class TimerClient

{ public: virtual void TimeOut() = 0;};

Если объект желает проинформировать о перерыве, вызывается функция Register для Timer. Аргументами этой функции являются время перерыва и указатель объекта TimerClient, чья функция TimeOut и вызывается по за­вершении перерыва. Каким образом связать класс TimerClient с классом TimedDoor, чтобы код в TimedDoor уведомлял о наступившем перерыве? Существуют несколько альтернатив. На рис. проиллюстрировано примитивное решение. Структура сформирована таким образом, что классы Door и TimedDoor наследуются из TimerClient. Получаем гарантию, что TimerClient сможет зарегистриро­ваться с помощью Timer и получить сообщение TimeOut.