我正在尝试理解并应用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的内容建议使用接口。
谢谢!
答案 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种类型的武器: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的框架:
答案 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原则,使代码更清晰,更容易支持,让您避免以后难以修复的错误。