2

我正在尝试理解和应用 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 的东西建议使用接口。

谢谢!

4

2 回答 2

2

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

  • 高级模块不应该依赖于低级模块。两者都应该依赖于抽象。
  • 抽象不应该依赖于细节。细节应该取决于抽象。

如您所见,它说should而不是must。它并不禁止你做任何事情。

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

假设您的系统由一千个类组成,您可以将 DIP 应用于 2 个类,其中一个依赖于第 3 个特定类(不是interfaces / abstract classes)。只要你的问题解决了,什么都不重要。因此,请尽量使您的解决方案简短而简单-> 易于理解。相信我,当您回顾您的解决方案 1 个月后,您会发现它很有价值。

DIP 是一种指导方针,它告诉您当您面临一组特定的问题以解决这些问题时该怎么做。这不是一个神奇的指南,它是有代价的:复杂性。您应用 DIP 的次数越多,您的系统就越复杂。所以明智地使用它。为了进一步支持这一点,我建议您看一下这个参考资料(取自Head First: Design Patterns书中)。访问此链接(它是一个 PDF 文件)并在顶部栏导航到 page 635 / 681。或者,如果您足够懒惰,请阅读以下引用:

你对模式的看法

初学者到处使用模式。这很好:初学者可以获得大量使用模式的经验和练习。初学者还认为,“我使用的图案越多,设计就越好。” 初学者会知道并非如此,所有设计都应该尽可能简单。复杂性和模式应该只在实际可扩展性需要的地方使用。

随着学习的进展,中级思维开始看到哪里需要模式,哪里不需要。中性思维仍然试图将太多的方形图案放入圆孔中,但也开始看到可以调整图案以适应规范图案不适合的情况。

禅心能够看到它们自然适合的模式。禅心不执着于使用模式;相反,它寻找最能解决问题的简单解决方案。禅心根据对象原则及其权衡来思考。当对一种模式的需求自然出现时,禅心就会应用它,因为它很清楚它可能需要适应。禅心还可以看到与相似模式的关系,并理解相关模式意图差异的微妙之处。禅心也是初学者的心——它不会让所有的模式知识过度影响设计决策。

最后,我会指出一个使用 DIP 的四人组设计模式:策略

示例问题:ACharacter可以使用 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 的设计模式和框架:

于 2016-11-13T21:36:40.773 回答
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::registerServcieDIPLSPISP

直接传递实例的另一个缺点是ServiceClass现在你不能保证它MainLogger不依赖于内部结构(访问其他方法/成员等)。

还有一点是代码维护的便利性——当你看到接口通过时,你只需要查看两个对象之间通信的“协议”。在使用具体类时,您实际上需要通过实现来了解如何使用传递的对象。

所以总的来说,最好尽可能遵循 SOLID 和其他 OOP 原则,它使代码更清晰,更容易支持,让您避免以后难以修复的错误。

于 2016-11-13T20:40:09.853 回答