Yves

设计模式六大原则

原则是死的,人是活的。

原则只是参考,如果违背了原则,项目也未必会失败,不遵循是不对的,严格执行就是“过犹不及”,不能为了套用原则而做项目。

单一职责原则(Single Responsibility Principle,SRP)

定义

有且仅有一个原因引起类的变更。即一个类只干一件事情。

就是这么简单,但是还有一个问题,就是职责的定义————什么是类的职责,以及怎么划分职责,职责是不是应该再细化,细化之后是不是都需要有一个接口或类。这些都需要从实际的项目中去考虑。单一职责原则最难划分的就是职责。

对于接口,在设计的时候一定要做到职责单一,但是对于实现类来说,需要“因地制宜”,视具体情况而定。生搬硬套单一职责原则会造成类的数量增加,人为增加系统的复杂性,给维护带来麻烦。原则是死的,人是活的。

优点:

  • 类的复杂性降低
  • 可读性提高
  • 可维护性提高
  • 变更引起的风险降低

里氏替换原则(Liskov Substitution Principle,LSP)

定义

所有引用基类的地方必须能透明地使用其子类的对象。也就是说,所有父类出现的地方子类就可以出现,而且替换为子类也不会产生任何错误或异常,使用者甚至可以不需要知道是父类还是子类。但是反过来就不行了。

简而言之,就是子类可以扩展父类的功能,但不能修改父类已经实现好的功能。

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

优点:

  • 复用代码
  • 提高代码的可扩展性
  • 提高项目的开放性

缺点:

  • 继承是入侵性的。只要继承,就必须拥有父类的所有属性和方法
  • 降低了代码的灵活性
  • 增加了耦合性

依赖倒置原则(Dependence Inversion Principle,DIP)

定义

  • 高层模块不应该依赖低层模块,两者都应该依赖其抽象
  • 抽象不应该依赖细节
  • 细节应该依赖抽象

简单来说,就是面向接口编程或面向抽象编程。

依赖倒置原则的本质就是通过抽象(接口或抽象类)使各个类或模块的实现彼此独立,不互相影响,实现模块间的解耦。

基本规则:

  • 每个类尽量都有接口或抽象类,或者两者兼备
    这是依赖倒置的基本要求,有了抽象才能倒置
  • 形参尽量是接口或抽象类
  • 减少从具体类中派生
  • 尽量不要重写基类中已经实现的方法

优点:

  • 可扩展性好
  • 耦合度低

接口隔离原则(Interface Segregation Principle,ISP)

定义

客户端不应该依赖它不需要的接口;一个类对另一个类的依赖应该建立在最小的接口上。

简单来说,就是接口尽量细化,同时接口中的方法尽可能少。当一个接口太大是,我们需要将它分割成一些更小的接口,使用该接口的客户端仅需要知道与之相关的方法即可。

UML

不遵循接口隔离原则的设计:

遵循接口隔离原则的设计:

接口隔离原则和单一职责原则的区别:

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

注意事项:

  • 接口尽量小,但是要有限度。
    对接口进行细化可以提高程序设计灵活性是不挣的事实,但是如果过小,则会造成接口数量过多,使设计复杂化。所以一定要适度。
  • 为依赖接口的类定制服务,只暴露给调用的类它需要的方法,它不需要的方法则隐藏起来。只有专注地为一个模块提供定制服务,才能建立最小的依赖关系。
  • 提高内聚,减少对外交互。使接口用最少的方法去完成最多的事情。

运用接口隔离原则,一定要适度,接口设计的过大或过小都不好。设计接口的时候,只有多花些时间去思考和筹划,才能准确地实践这一原则。

迪米特法则(Law of Demeter,LoD)/最少知识原则(Least Knowledge Principle,LKP)

定义

一个对象应该对其他对象有最少的了解。也就是说,一个类应该对自己需要耦合或调用的类知道的最少,被耦合调用的类内部是如何复杂都和我没关系。

要求

迪米特法则对类的低耦合提出了明确的要求:

  • 只和朋友交流
    每个对象都必然会与其他对象有耦合关系,耦合的对象中出现在成员变量、方法的输入输出参数中的类成为成员朋友类,而出现在方法体内部的类不属于朋友类。这种关系的类型有很多,例如组合、聚合、依赖等。
  • 朋友间也是有距离的
    一个类公开的public属性或方法越多,修改时设计的面也就越大,变更引起的风险扩散也就越大。因此为了保持朋友类间的距离,在设计时需要反复衡量,是否还可以再减少public方法和属性,是否可以修改为private、package-private、protected等访问权限,是否可以加上final关键字等
  • 是自己的就是自己的
    在实际应用中经常会出现这样的方法:放在本类中也可以,放在其他类中也没有错。对这种情况可以坚持一个原则:如果一个方法放在本类中,即不增加类间关系,也不对本类产生负面影响,就放在本类中。

总结

迪米特法则的核心观念是类间解耦,弱耦合。其要求的结果就是产生了大量的中专或跳转类,导致系统的复杂性提高,也为维护带来了难度。所以在采用迪米特法则的时候需要反复权衡,既要做到让结构清晰,又要做到高内聚低耦合。

开闭原则(Open Closed Principle,OCP)

定义

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

在软件的生命周期内,因为变化、升级、维护等原因,需要对软件原有代码进行修改是,可能会给旧代码引入错误。因此,当软件需要变化时,我们应该尽量通过扩展的方式来实现变化,而不是通过修改已有的代码来实现。

开闭原则是一个非常”虚”的原则,实际上前面的五个原则就是对开闭原则的具体解释,但开闭原则并不局限于这几个。