2016-11-13 12 views
2

Я пытаюсь понять и применить принципы SOLID. Что касается принципа инверсии зависимостей, означает ли это, что композиция/агрегация объекта запрещена? Для того чтобы интерфейс всегда использовался для доступа к методу другого класса?SOLID: означает ли DIP, что композиция/агрессия для объекта запрещена?

Я имею в виду, что:

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, предлагает использовать интерфейсы.

Спасибо!

ответ

2

В принципе, основная идея DIP является:

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

Как вы можете видеть, это говорит должен и не обязательным. Это не запрещает вам ничего делать.

Если ваши классы composite of/aggregate to другие специфические classes (не interfaces/abstract classes), это нормально! Ваш код все еще будет компилироваться и запускаться, никакие предупреждения не будут отображаться, говоря вам: «Эй, вы нарушаете DIP». Поэтому я думаю, что ответ на ваш вопрос: Нет, это не.

Представьте, что ваша система состоит из тысячи классов, вы можете просто применить DIP к 2 классам и иметь один из них, зависит от 3-го конкретного класса (не interfaces/abstract classes). Пока ваша проблема решена, ничего не имеет значения. Поэтому старайтесь держать свое решение коротким & простым -> легко понять. Поверьте мне, вы найдете это ценным через месяц после того, как вы оглянетесь на свое решение.

DIP - это руководство, которое говорит вам, что делать, когда вы сталкиваетесь с определенным набором проблем, чтобы решить их. Это не волшебный ориентир, он стоит за счет: сложность. Чем больше вы применяете DIP, тем сложнее будет ваша система. Поэтому используйте его с умом. Чтобы поддержать этот момент, я рекомендую вам ознакомиться с этой ссылкой (взято из книги Head First: Design Patterns). Получите доступ к этому link (это файл в формате PDF) и в верхней строке перейдите на страницу 635/681. Или, если вы достаточно ленивы, просто прочитать ниже цитату:

Your Mind на Patterns

новичку использует шаблоны везде. Это хорошо: новичок получает много опыта и использует шаблоны. Новичок также считает, что «чем больше узоров я использую, тем лучше дизайн». Новичок начинающих узнает, что это не так, что все проекты должны быть как можно проще . Сложность и модели следует использовать только там, где они необходимы для практической расширяемости.

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

Ум Zen способен видеть узоры, в которых они подходят естественным образом.Ум Дзена не одержим использованием шаблонов; скорее, он ищет простых решений, которые наилучшим образом решают проблему. Разум Дзэн думает в терминах принципов объекта и их компромиссов. Когда возникает потребность в шаблоне , разум Дзэн применяет его, хорошо зная, что ему может потребоваться адаптация . Разум Дзэн также видит отношения к подобным шаблонам и понимает тонкости различий в намерениях связанных шаблонов. Разум Дзэн также является сознанием начинающего - он не позволяет всему этому знанию шаблона чрезмерно влиять на дизайн решений.

Наконец, я укажу вам на Банды Четырех шаблон проектирования, которые делают использование DIP: Strategy

Пример задачи: A Character может использовать 3 вида оружия: Hand, Sword, & Gun , Он (Character) может поменять свое текущее оружие всякий раз, когда захочет.

Анализ: Это очень типичная проблема. Трудная часть заключается в том, как обрабатывать обмен оружием во время выполнения.

кандидат решение со Стратегией: (всего SketchUp):

enter image description here

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:

+0

Большое вам спасибо за ваш ответ! У меня такое чувство, что ты полностью заботишься. Часть, касающаяся использования опыта и дизайна, вероятно, сэкономит мне много времени. Вероятно, вы заставите меня сэкономить много времени, блуждая между различными способами использования шаблонов дизайна. Большое спасибо за ваш ответ :). – Plouff

+0

Я думал, что чем сложнее система, тем больше вы будете применять DIP. –

+0

@AdrianIftode: Если применение DIP сделало систему менее сложной, я бы назвал ее «плохо разработанной» системой, а не «сложной». Здесь это справочный материал, в котором упоминается, как система может быть более сложной при установке шаблонов проектирования: https://youtu.be/gwIS9cZlrhk?t=4153 –

1

Да, это правильно, 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 (поскольку у нас явно есть разные интерфейсы здесь - один, чтобы сообщить результаты, а другой - полезную работу), и вы также будете нарушать еще одно хорошее правило, чтобы предпочесть composition over inheritance.

Еще один недостаток передачи экземпляра ServiceClass прямо сейчас, поскольку вы не можете гарантировать, что MainLogger не полагается на внутреннюю структуру (доступ к другим методам/членам и т. Д.).

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

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

+0

Большое спасибо за ваш ответ тоже. Вы разъяснили значение DIP, поэтому теперь я могу сказать, что я это понимаю. Надеюсь, я также научусь быстро, когда использовать его «мудро». – Plouff