Я пытаюсь понять и применять твердые принципы.
Относится ли принцип инверсии зависимости, означает ли это, что составление / агрегирование объекта запрещено?
Чтобы интерфейс всегда использовался для доступа к другому методу класса?
Я имею в виду, что:
class ServiceClass {
void serviceClasshelper();
}
class MainClass {
void MainClass(ServiceClass service); // To use serviceClasshelper
}
Должен быть изменен на:
class ServiceInterface {
virtual void interfaceHelper() =0;
}
class ServiceClass : public ServiceInterface {
void serviceClasshelper();
void interfaceHelper() { serviceClasshelper(); };
}
class MainClass {
void MainClass(ServiceInterface service); // Uses interfaceHelper
}
Я думаю (или, по крайней мере, я надеюсь), что я понимаю принцип.
Но интересно, если это можно перефразировать так.
Действительно, материал, который я читал о DIP, предлагает использовать интерфейсы.
Спасибо!
По сути, основная идея DIP:
- Модули высокого уровня должен не зависит от низкоуровневых модулей. И то и другое должен зависит от абстракций.
- Абстракции должен не зависит от деталей. подробности должен зависит от абстракций.
Как видите, написано должен и не должен. Это не запрещает вам делать что-либо.
Если ваши занятия composite of / aggregate to
другие специфические classes
(не interfaces / abstract classes
) это нормально! Ваш код по-прежнему будет компилироваться и запускаться, не будет отображаться никаких предупреждений о том, что «эй, вы нарушаете DIP». Поэтому я думаю, что ответ на ваш вопрос: Нет не.
Представьте, что ваша система состоит из тысячи классов, вы можете просто применить DIP к 2 классам, и один из них зависит от 3-го конкретного класса (не interfaces / abstract classes
). Пока ваша проблема решена, ничего не имеет значения. Поэтому постарайтесь, чтобы ваше решение было коротким & просто -> легко понять. Поверьте мне, вы найдете это ценным через 1 месяц после того, как вы оглянетесь на свое решение.
DIP — это руководство, которое говорит вам, что делать, когда вы сталкиваетесь с определенным набором проблем, чтобы решить их. Это не волшебный ориентир, он стоит дорого: сложность. Чем больше вы применяете DIP, тем сложнее будет ваша система. Так что используйте это с умом. Для дальнейшей поддержки этого пункта, я рекомендую вам взглянуть на эту ссылку (взято из Head First: Design Patterns
книга). Доступ к этому ссылка на сайт (это файл PDF) и в верхней панели перейдите на страницу 635 / 681
, Или, если вы достаточно ленивы, просто прочитайте цитату ниже:
Ваш разум на шаблонах
Новичок везде использует шаблоны. Это хорошо: новичок получает большой опыт и практикуется с использованием шаблонов. Новичок
также думает: «Чем больше шаблонов я использую, тем лучше дизайн».
начинающий узнает, что это не так, что все дизайны должны быть такими
как можно проще Сложность и шаблоны должны использоваться только там, где
они необходимы для практической расширяемости.По мере продвижения обучения Промежуточный разум начинает видеть, где нужны шаблоны, а где нет. Промежуточный разум
до сих пор пытается поместить слишком много квадратных узоров в круглые отверстия, но также
начинает видеть, что шаблоны могут быть адаптированы к ситуации, когда
канонический рисунок не подходит.Разум дзен способен видеть шаблоны там, где они подходят. Ум дзен не одержим использованием шаблонов; скорее он ищет
простые решения, которые лучше всего решают проблему. Разум Дзэн мыслит в
Условия объекта принципов и их компромиссы. Когда потребность в
естественно возникает паттерн, разум дзен применяет его, хорошо зная, что он
может потребоваться адаптация. Ум дзен также видит отношения с
аналогичные модели и понимает тонкости различий в
Намерение связанных моделей. Ум дзен — это тоже ум новичка — он
не позволяет всем этим знаниям шаблона чрезмерно влиять на дизайн
решения.
Наконец, я укажу вам шаблон проектирования Gang of Four, использующий DIP: стратегия
Пример задачи: A Character
Можно использовать 3 вида оружия: Hand
, Sword
, & Gun
, Он (Character
) может поменять свое текущее оружие, когда захочет.
АнализЭто очень типичная проблема. Сложная задача — как обмениваться оружием во время выполнения.
Кандидатское решение со стратегией: (просто набросок):
weapon = new Hand();
weapon.Attack(); // Implementation of Hand class
weapon = new Sword();
weapon.Attack(); // Implementation of Sword class
weapon = new Gun();
weapon.Attack(); // Implementation of Gun class
Другие шаблоны дизайна & рамки, которые используют DIP:
Да, это верно, DIP говорит, что вам нужно зависеть от абстракции (интерфейса), а не от конкреции (класса с реальной реализацией).
Идея в том, что если вы зависите от ServiceClass
Вы зависите от этой конкретной реализации и не можете легко заменить ее другой реализацией. Например, у вас есть AnotherServiceClass
, но использовать его вместо ServiceClass
, вам придется унаследовать его от ServiceClass
что может быть нежелательно или даже невозможно. Имея зависимость от интерфейса, вы можете легко это сделать.
Обновление: вот более конкретный пример, иллюстрирующий идею выше
// Service class does something useful (sendNetworkRequest)
// and is able to report the results
class ServiceClass {
void reportResults();
void sendNetworkRequest();
}
// Main logger collects results
class MainLogger {
void registerService(ServiceClass service);
}
У нас есть ServiceClass
который мы переходим к MainLogger::registerService
регистратор (например) вызывает service->reportResults()
периодически и сохраняет в файл.
Теперь представьте, что у нас есть другой сервис:
// Service class does something useful (calculateYearlyReport)
// and is able to report the results
class AnotherServiceClass {
void reportResults();
void calculateYearlyReport();
}
Если мы просто используем конкретные классы здесь и наследуем AnotherServiceClass
от ServiceClass
мы сможем передать его MainLogger::registerServcie
но кроме нарушения DIP
мы тоже будем нарушать LSP
(поскольку подкласс не может использоваться в качестве замены BaseClass) и ISP
(поскольку у нас явно есть разные интерфейсы — один для отчета о результатах, а другой для полезной работы), и вы также нарушите еще одно хорошее правило, предпочитая состав по наследству.
Еще один недостаток передачи экземпляра ServiceClass
прямо как сейчас, вы не можете гарантировать, что MainLogger
не полагается на внутреннюю структуру (доступ к другим методам / членам и т. д.).
Еще одним моментом является простота обслуживания кода — когда вы видите, что интерфейс передан, вам просто нужно просмотреть «протокол» связи между двумя объектами. В то время как с конкретным классом, вам на самом деле нужно пройти через реализацию, чтобы понять, как используется переданный объект.
Таким образом, в общем случае лучше следовать SOLID и другим принципам ООП, где это возможно, это делает код чище и проще в поддержке, а также позволяет избежать ошибок, которые впоследствии будет трудно исправить.