设计模式的Solid原则有:
- Single Responsibility Principle:单一职责原则
- Open Closed Principle:开闭原则
- Liskov Substitution Principle:里氏替换原则
- Interface Segregation Principle:接口隔离原则
- Dependence Inversion Principle:依赖倒置原则
除此之外,还有:
- Law of Demeter:迪米特法则
- Composite/Aggregate ReusePrinciple :合成复用原则
链接:https://www.jianshu.com/p/3268264ae581
一、单一职责原则(SRP)
“引起类变化的因素永远不要多于一个。” 或者说 “一个类有且仅有一个职责”
这里的"单一职责"就是我们通常所说的“高内聚”,即一个类只完成它应该完成的职责,不能推诿责任,也不可越殂代疱,不能成为无所不能的上帝类。
这是一个违反了“单一职责原则” 的类结构图。
这里,Rectangle类做了下面两件事:
- 计算矩形面积;
- 在界面(绘制设备)上绘制矩形;
这违反了SRP(单一职责原则)。因为Rectangle类做了两件事,在一个方法里它计算了面积,在另外一个方法了它返回一个表示矩形的GUI。
应该拆分职责到两个不同的类中,如:
- Rectangle: 这个类应该只定义Area()方法;
- RectangleUI: 这个类应继承Rectangle类,并定义Draw()方法。
二、开放封闭原则 (OCP)
“软件实体(类,模块,函数等等)应当对扩展开放,对修改闭合。”
通俗来讲,它意味着你(或者类的客户)应当能在不修改一个类的前提下扩展这个类的行为。我们要将系统中可能变化的地方封装起来,即对修改封闭。
同时,为了应对系统需求(功能)的扩展,需要抽象!《设计模式》中的state模式和strategy模式是这个原则的最好体现。
违反了开放封闭原则的类结构图。
客户端代码直接面向服务器端的具体实现编程,缺乏灵活性。这样如果服务器因为某些原因被其他服务器替换了,那么客户端调用服务器的代码也必须做相应的修改或替换。这其实就是”面向实现编程“的设计臭味!
那么,如何修改才能得到正确灵活的设计?
答案是:抽象!为服务器端的代码(类型)抽象出一个抽象基类(定义一组完成服务职责的最小接口)。
下面是正确的设计:
三、Liskov’s 替换原则(LSP)
"子类型必须能够替换它们的基类型。"或者换个说法:“使用基类引用的地方必须能使用继承类的对象而不必知道它。”
这里,KingFisher(翠鸟)类扩展了Bird基类,并继承了Fly()方法,这没有问题。
但是下面这个类结构图就存在设计上的问题:
Ostrich(鸵鸟)是一种鸟,这毋庸置疑,并从Bird类继承,这从概念上说没有问题。但是鸵鸟它能飞吗?不能,那么这个设计就违反了LSP。因为在使用Bird的地方不一定能用Ostrich代替。
所以,即使在现实中看起来没问题,在类设计中,Ostrich不应该从Bird类继承,这里应该从Bird中分离一个不会飞的类NoFlyBrid,Ostrich应该继承这个不会飞的鸟类NoFlyBrid。
为什么LSP如此重要?
- 如果没有LSP,类继承就会混乱;如果子类作为一个参数传递给方法,将会出现未知行为;
- 如果没有LSP,适用与基类的单元测试将不能成功用于测试子类;
四、接口分离原则(ISP)
“客户端不应该被迫依赖于它们不用的接口。” 或者说:“软件系统模块的粒度尽可能少,以达到高度可重用的目的。”
接口包含太多的方法会降低其可用性,像这种包含了无用方法的"胖接口"会增加类之间的耦合。如果一个类想实现该接口,那么它需要实现所有的方法,尽管有些对它来说可能完全没用,所以这样做会在系统中引入不必要的复杂度,降低代码的可维护性。
如果一个接口包含了过多的方法,应该通过分离接口将其拆分。
这是一个违反接口分离原则的胖接口。
注意到IBird接口包含很多鸟类的行为,包括Fly()行为.现在如果一个Bird类(如Ostrich)实现了这个接口,那么它需要实现不必要的Fly()行为(Ostrich不会飞)。因此,这个"胖接口"应该拆分成两个不同的接口,IBird和IFlyingBird, 而IFlyingBird继承自IBird。如下图所示:
这样的话,重用将变得非常灵活:如果一种鸟不会飞(如Ostrich),那它实现IBird接口。如果一种鸟会飞(如KingFisher),那么它实现IFlyingBird。
五、依赖倒置原则(DIP)
这个原则的意思是:高层模块不应该依赖底层模块,两者都应该依赖其抽象。其实又是”面向接口编程,不要面向实现编程“的内在要求。
汽车是由很多如引擎,车轮,空调和其它等部件组成:
这里的 Car 就是高层模块;它依赖于抽象接口IToyotaEngine 和 IEighteenInchWheel.
而具体的引擎FifteenHundredCCEngine 属于底层模块,也依赖于抽象接口IToyotaEngine ;
具体的车轮 EighteenInchWheelWithAlloy同样属于底层模块,也依赖于抽象接口IEighteenInchWheel。
上面Car类有两个属性(引擎和车轮列表),它们都是抽象类型(接口)。引擎和车轮是可插拔的,因为汽车能接受任何实现了声明接口的对象,并且Car类不需要做任何改动。
六、迪米特法则(LoD / LKP)
只与你的朋友们通信,不要与“陌生人”说话。
七、合成复用原则(CARP)
尽量使用对象组合,而不是继承关系达到软件复用的目的。
循序这一原则通常也是避免触犯里氏替换原则所要求的。
原文链接:https://blog.csdn.net/e5max/article/details/8872182