Skip to content

Latest commit

 

History

History
146 lines (77 loc) · 8.08 KB

六大设计原则.md

File metadata and controls

146 lines (77 loc) · 8.08 KB

六大设计原则

1 六大设计原则(SOLID)

六大设计原则分别是:

  • 单一职责原则
  • 开闭原则
  • 里氏替换原则
  • 迪米特原则
  • 接口隔离原则
  • 依赖倒置原则

六个原则的首字母合起来(两个L算一个)就是SOLID(稳定的单词),代表着稳定、灵活、健壮的设计。

1.1 单一职责原则

一个类应该只有一个发生变化的原因。

定义对象间的一对多依赖的关系,使得每当一个对象状态发生状态改变时,其相关依赖对象都可以得到通知。

简单来说,就是一个类只负责一个职责。每个类只负责自己的那部分,复杂度降低,易维护。如果一个类中有多项功能将会非常臃肿,而且一旦出现bug,要在所有代码中去寻找。更改一个地方可能会改变整个代码的结构。

单一职责原则不仅仅适用于类,对于接口和方法也适用,即一个接口/方法,只负责一件事,易读,便于维护。

不过事实上,类的单一职责在项目中是很难保证的,接口和方法更容易实现一些。

单一职责的好处:

  • 代码颗粒度降低,类的复杂度降低。

  • 可读性提高,清晰知道每个类的职责。

  • 可维护性提供,能快速定位bug位置。

  • 降低改动代码的风险。

1.2 开闭原则

一个软件,如类、模块和函数应该对扩展开放,对修改关闭。

开闭原则是面向对象设计中最基础的设计原则,只定义对外修改关闭,对扩展开放。其实只要遵循SOLID中的另外5个原则,设计出来的软件就是符合开闭原则的。

当软件需要变化时,尽量通过扩展来实现,而不是通过修改已有的代码来实现。用抽象构建架构,用扩展实现细节。抽象灵活性好,适用性广,只要抽象合理,可以基本保证架构的稳定。软件中易变的细节,可以用抽象派生的实现类来进行扩展,当软件需要变化时,我们只需要根据需求重新派生一个类来扩展实现即可。当然前提是抽象要合理,要对需求的变更有前瞻性和预见性。

1.3 里氏替换原则

所有引用基类的地方必须能透明地使用其子类的对象。

里氏替换原则的意思是,所有使用基类的地方,都可以换成子类,程序还可以正常运行,这个原则与面向对象语言的继承特性密切相关。

继承存在一些缺点,只要继承,就必须拥有所有属性和方法,降低了代码的灵活性。并增加了耦合性,当需要对父类的代码进行修改时,必须考虑到对子类产生的影响。

里氏替换原则对继承进行了规则上的约束,主要体现在四个方面

  • 子类必须实现父类的抽象方法,但不得重写(覆盖)父类的非抽象(已实现)方法。
  • 子类中可以增加自己特有的方法。
  • 当子类覆盖或实现父类的方法时,方法的前置条件(即方法的行参)要比父类方法的输入参数更宽松。(即只能重载不能重写)
  • 当子类的方法实现父类的抽象方法时,方法的后置条件(即方法的返回值)要比父类更严格。

最后两条,在OC语言下因为没有重载的方式,改变不了行参的定义,所以并没有后两条约束。

1.4 迪米特原则

只与你的直接朋友交谈,不跟“陌生人”说话。

如果两个软件实体无须直接通信,那么就不应当发生直接的相互调用,可以通过第三方转发该调用。其目的是降低类之间的耦合度,提高模块的相对独立性。

迪米特原则要求软件实体之间通信的宽度和深度,正确使用迪米特原则有有两个优点

  • 降低了类之间的耦合度,提高了模块的相对独立性。
  • 由于亲合度降低,从而提高了类的可复用率和系统的扩展性。

但同时使用该法则有可能产生大量的中介类,从而增加系统的复杂性,使模块之间的通信效率降低,所以需要反复衡量,在确保高内聚和低耦合的同时,保证系统的结构清晰。

通过定义和特点可知,它强调两点

  • 从依赖者的角度来说,只依赖应该依赖的对象。
  • 从被依赖者的角度来说,只暴露应该暴露的方法。

法则的现实比喻:客户只要找到中介,寻求满足要求的楼盘,而不必跟每个楼盘发生联系。

1.5 接口隔离原则

  1. 客户端不应该依赖它不需要的接口。
  2. 类间的依赖关系应该建立在最小的接口上。

要为各个类建立它们需要的专用接口,而不要试图去建立一个很庞大的接口供所有依赖它的类去调用。

(该原则中的接口,是一个泛泛而言的接口,不仅仅指各种计算机语言中的接口,还包括其中的抽象类)

从定义上看接口隔离原则和单一职责都是为了提高类的内聚性、降低它们之间的耦合性,体现了封装的思想,但两者是不同的

  • 单一职责原则注意的是职责,而接口隔离原则则注重的是对接口依赖的隔离。
  • 单一职责原则主要是约束类,它针对的是程序中的实现和细节;接口隔离原则主要约束接口,主要针对抽象和程序整体框架的构建。

遵循接口隔离原则有以下5个优点

  • 将臃肿庞大的接口分解为多个粒度小的接口,可以预防外来变更的扩散,提高系统的灵活性和可维护性。
  • 接口隔离提高了系统的内聚性,减少了对外交互,降低了系统的耦合性。
  • 如果接口的粒度大小定义合理,能够保证系统的稳定性;定义过小,则会造成接口数量过多,使设计复杂化;如果定义太大,灵活性降低,无法提供定制服务,给项目带来风险。
  • 使用多个专门接口还能体现对象的层次,因为可以通过接口的继承,实现对接口的定义。
  • 能减少项目中代码的冗余。过大的接口里面通常放置许多不用的方法,当实现这个接口的时候,被迫设计冗余的代码。

在具体遵循接口隔离原则时,应该根据以下几个规则来衡量:

  • 拆分接口时,首先必须满足单一职责原则。
  • 接口尽量小,但是要有限度。一个接口只服务于一个子模块或业务逻辑。
  • 为依赖接口的类定制服务。只提供调用者需要的方法,屏蔽不需要的方法。
  • 了解环境,拒绝盲从。每个项目或产品都有选定的环境因素,接口拆分的标准不同,需要深入了解业务逻辑。
  • 提高内聚,减少对外交互。使接口用最少的方法去完成最多的事情。

1.6 依赖倒置原则

上层模块不应该依赖底层模块,它们都应该依赖于抽象。

抽象不应该依赖于细节,细节应该依赖于抽象。

这个原则听起来很像“针对接口编程,不针对实现编程”,但这个接口更强调“抽象”。

这里通过披萨店的例子来说明依赖倒置。

如下图,披萨店是上层模块,各种披萨是下层模块。

先从顶端开始,然后往下到具体类,但是我们并不想让披萨店直接理会这些具体披萨类,要不然披萨店就得全部依赖。然后此时需要从下层开始,进行抽象。因为是都是披萨,所以我们可以抽象出一个披萨的抽象类。现在重新设计后如下图:

从两图中看,图一依赖箭头从上往下,现在图二的箭头出现了从下往上,依赖关系确实“倒置”了。

同时也解释了“上层模块不应该依赖底层模块,它们都应该依赖于抽象”。在最开始上层直接依赖底层,重新设计后,上层模块和底层模块都依赖于抽象层。