SOLID:DIP是否意味着禁止对象的组合/聚合?

时间:2016-11-13 11:40:42

标签: c++ oop solid-principles dependency-inversion

我正在尝试理解并应用SOLID原则。 关于依赖倒置原则,是否意味着禁止对象的组合/聚合? 那么必须始终使用接口来访问另一个类方法吗?

我的意思是:

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 个答案:

答案 0 :(得分:2)

基本上,DIP的主要思想是:

  
      
  • 高级模块不依赖于低级模块。 都应该依赖于抽象。
  •   
  • 抽象不依赖于细节。详细信息依赖于抽象。
  •   

正如您所看到的,它表示应该而不是必须。它不禁止你做任何事情。

如果您的课程composite of / aggregate to其他特定classes(不是interfaces / abstract classes),那就没问题!您的代码仍将编译并运行,不会显示任何警告,告诉您:“嘿,您违反了DIP”。所以我认为你的问题的答案是:不,它没有

想象一下,您的系统由一千个类组成,您可以将DIP应用于两个类,其中一个类依赖于第三个特定类(不是interfaces / abstract classes)。只要你的问题得到解决,没有任何问题。所以尽量保持你的解决方案简短简单 - >容易明白。相信我,当你回顾你的解决方案一个月后,你会发现它很有价值。

DIP是一个指导原则,它告诉您在面对一系列特定问题时如何解决问题。它不是一个神奇的指导方针,它需要付出代价:复杂性。应用DIP越多,系统就越复杂。所以明智地使用它。为了进一步支持这一点,我建议你看看这个参考(摘自Head First: Design Patterns书)。访问此link(这是一个PDF文件),在顶部栏中导航到页面635 / 681。或者,如果你足够懒,请阅读以下引用:

  

你对模式的看法

     

初学者到处使用模式。这很好:初学者获得了很多使用模式的经验和练习。初学者   也认为,“我使用的模式越多,设计就越好。”   初学者将了解到并非如此,所有设计都应如此   尽可能简单。复杂性和模式应该只用在哪里   它们是实用可扩展性所必需的。

     

随着学习进展,中级思维开始看到需要哪些模式以及不需要的模式。中级思维   仍试图将太多方形图案装入圆孔中,但也是如此   开始看到模式可以适应适合的情况   规范模式不合适。

     

禅意能够看到自然适合的模式。禅意并不痴迷于使用模式;而它寻找   最能解决问题的简单解决方案。禅宗思想在思考   对象原则的术语及其权衡取舍。当需要一个   自然而然地出现了一种模式,禅宗的思想很好地应用它   可能需要改编。禅宗思想也看到了与人的关系   类似的模式,并理解差异的微妙之处   相关模式的意图。禅意也是初学者的心灵 - 它   不要让所有模式知识过度影响设计   决定。

最后,我将指出一个使用DIP的四人帮设计模式:Strategy

示例问题Character可以使用3种类型的武器:HandSword和& Gun。他(Character)可以随时交换他当前的武器。

分析:这是一个非常典型的问题。棘手的部分是如何在运行时处理武器交换。

使用策略

候选解决方案 :(只是草图):

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的框架:

答案 1 :(得分: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和其他OOP原则,使代码更清晰,更容易支持,让您避免以后难以修复的错误。