Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Статический анализ программ.doc
Скачиваний:
32
Добавлен:
01.05.2014
Размер:
82.43 Кб
Скачать

Инвертирование зависимостей при объектно-ориентированном проектировании.

Заметим, что в приведенной проблеме модуль, описывающий поведение высокого уровня (в примере это модуль Copy), зависит от деталей такого поведения. Если бы мы могли сделать такой модуль менее зависимым от деталей его поведения, такой модуль можно было бы использовать повторно. Можно было бы без ограничений создавать программы, которые используют модульCopyдля копирования с любого устройств ввода на любое устройства вывода. Методы объектно-ориентированного проектирования позволяют нам сделать такое инвертирование зависимостей.

Рассмотрим следующую диаграмму классов:

На диаграмме класс Copyхранит указатели на абстрактные классыReaderиWriter. Можно представить цикл в классеCopy, который получает символы от классаReaderи посылает их классуWriter. Однако классCopyсовершенно не зависит от классовReadKeyboardиWritePrinter. Таким образом, эти   зависимости были инвертированы. Теперь классCopyзависит от абстракций. Классы, знающие о деталях чтения и записи, также зависят от тех же абстракций.

Класс Copyтеперь готов к повторному использованию. Можно создавать неограниченное число новых разновидностей классов для чтения и записи, и предоставлять их классуCopy. Ни от одного из них классCopyзависеть не будет. Зависимостей, делающих этот класс "твердым" и "хрупким", уже нет.

Хорошие зависимости

Что делает объектно-ориентированную версию программы копирования устойчивой к ошибкам, сопровождаемой и повторно используемой? Отсутствие в нем лишних зависимостей. Тем не менее, некоторые зависимости в программе существуют, но не влияют на необходимое качество программы. Почему не влияют? Потому что цели этих зависимостей очень стабильны. Их изменение маловероятно.

Рассмотрим природу классов WriterиReader. На С++ они могли быть представлены следующим образом:

class Writer {public: virtual void Write(char) = 0;};

class Reader {public: virtual char Read() = 0;};

Эти два класса вряд ли изменятся. Что может их заставить измениться? При нормальном ходе событий эти классы чрезвычайно устойчивы.

Таким образом, существует немного причин, которые вынудили бы изменить класс Copy. КлассCopyесть пример работы принципа "Открыт/закрыт".  Этот классоткрытдля расширения, поскольку можно создавать новые версии программ для чтения и записи. Этот классзакрытдля модификации. Поскольку нам не нужно изменять его, что бы добиться такого расширения. Поэтому можно сказать, чтохорошая зависимость- это зависимость от чего-то устойчивого. Чем стабильней цель этого отношения зависимости, тем лучше эта зависимость. И наоборот.

Стабильность

Как достичь стабильности? Почему классы WriterиReaderтак устойчивы? Рассмотрите снова причины, которые могли заставить их измениться. Они   вообще ни от чего не зависят, поскольку изменение в зависимых от них классах не сможет заставить их изменяться. Назовем это свойство классовнезависимостью. Независимые классы - те, которые не зависят от чего-нибудь еще.

Другая причина устойчивости классов WriterиReaderсостоит в том, что от них зависят многие другие классы. Например, классыCopy,KeyboardReaderиKeyboardWriter. Чем больше существует классов для чтения и записи, тем больше зависимых имеют классыWriterиReader. И тем сложнее их менять, поскольку тогда придется менять все зависимые от них классы. Эта очень веская причина,удерживающаянас от изменения таких классов, и заставляющая нас бороться за их стабильность.

Назовем такие классы ответственными. Ответственные классы имеют тенденцию к стабильности, поскольку любое их изменение вызовет большие последствия.

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