面向对象的设计原则(SOLID)

设计模式的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

发布了67 篇原创文章 · 获赞 32 · 访问量 6万+

猜你喜欢

转载自blog.csdn.net/weixin_43751710/article/details/105065319
今日推荐