这篇文章介绍下著名的设计原则:SOLID
- 单一职责是所有设计原则的基础
- 开闭原则是设计的终极目标
- 里氏替换原则强调的是子类替换父类后程序运行时的正确性,它用来帮助实现开闭原则
- 而接口隔离原则用来帮助实现里氏替换原则,同时它也体现了单一职责
- 依赖倒置原则是过程式编程与OO编程的分水岭,同时它也被用来指导接口隔离原则
单一职责原则SIP (Single Responsibility Principle)
每个软件模块都应该有且只有一个被改变的理由。一个类只能承担一件事。
开闭原则OCP (Open-closed Principle)
开闭原则指软件实体(类、模块等)应当对扩展开放,对修改闭合。允许扩展行为和无需修改原来的代码。
没有人能够在一开始就识别出所有扩展点,也不可能在所有地方都预留出扩展点,这么做的成本是不可接受的。因此一定是由需求变化驱动。如果你有领域专家的支持,他可以帮你识别出变化点。
实现开闭原则的关键是抽象。在Bertrand Meyer提出开闭原则的年代(上世纪80年代),在类库中增加属性或方法,都不可避免地要修改依赖此类库的代码。这显然导致软件很难维护,因此他强调的是要允许通过继承来扩展类。随着技术发展,我们有了更多的方法来实现开闭原则,包括接口、抽象类、策略模式等。
我们也许永远都无法完全做到开闭原则,但不妨碍它是设计的终极目标。
里氏替换原则LSP (Liskov Substitution Principle)
程序中的对象应该可以被其子类实例替换掉,而不影响程序的正确性。
接口隔离原则ISP (Interface Segregation Principle)
使用多个特定细分的接口比单一的总接口要好,不能强迫用户去依赖他们用不到的接口。
依赖倒置原则DIP (Dependence Inversion Principle)
程序要以来于抽象接口,而不是具体实现:
- 高层模块不应该依赖于底层模块,两者都应该依赖于抽象
- 抽象不应该依赖于具体实现,具体实现应该依赖抽象
依赖倒置原则是区分过程式编程和面向对象编程的分水岭。过程式编程的依赖没有倒置,A Simple DIP Example | Agile Principles, Patterns, and Practices in C#这篇文章以开关和灯的例子很好地说明了这一点。
上图的关系中,当Button直接调用灯的开和关时,Button就依赖于灯了。其代码完全是过程式编程:
public class Button {
private Lamp lamp;
public void Poll() {
if (/*some condition*/)
lamp.TurnOn();
}
}
如果Button还想控制电视机,微波炉怎么办?应对这种变化的办法就是抽象,抽象出Role interface ButtonServer:
不管是电灯,还是电视机,只要实现了ButtonServer,Button都可以控制。这是面向对象的编程方式。