面向对象设计原则

常用的设计原则有7个:

  • 单一职责原则
  • 开闭原则
  • 里氏替换原则
  • 依赖倒转原则
  • 接口隔离原则
  • 合成复用原则
  • 迪米特法则

单一职责原则

一个对象应该只包含单一的职责,并且该职责被完整地封装在一个类中

为什么要用单一职责原则:

  • 一个类承担的职责越多,被复用的可能性越小
  • 高内聚低耦合

开闭原则

一个软件实体应当对扩展开放,对修改封闭。即设计一个模块的时候应当使其可以在不被修改的前提下被扩展,实现在不修改源代码的情况下改变这个模块的行为。

  • 抽象化是开闭原则的关键
  • 找到系统的可变因素并将其封装起来

里氏替换原则

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

  • 说人话就是在软件中使用父类对象的地方一定要能够使用其子类对象
  • 里氏替换原则是实现开闭原则的重要方式之一,在程序中尽量使用父类类型来对对象进行定义,运行时再确定子类类型(即引用用父类,实例用子类),用子类对象来替换父类对象。

依赖倒转原则

高层模块不应该依赖低层模块,它们都应该依赖抽象。抽象不应该依赖于细节(实现),细节(实现)应该依赖于抽象

换一种说法是,面向接口编程而不是实现

  • 依赖应当指向接口而非具体实现
  • 实现开闭原则的关键是抽象化,并从抽象引导具体实现;如果开闭原则是目标,那么依赖倒转就是手段

接口隔离原则

客户端不应该依赖它不需要的接口

或者说,如果一个接口太大,则需要将它分割成更细小的接口,使用该接口的客户端仅需要知道与之相关的方法即可

有什么指导意义:

  • 使用多个专门接口,而不是单一的总接口,只干该干的,不干不该干的
  • 一个接口只代表一个角色,仅提供客户端需要的行为
  • 这首先需要满足单一职责原则

“定制服务”的思想:为不同客户的提供宽窄不同的接口,只提供需要的行为,隐藏不需要的

合成复用原则

尽量使用对象组合,而不是继承来达到复用的目的

怎么做:

  • 通过聚合或组合来使用已有对象(通过委派调用已有对象的方法达到复用其功能的目的),少用继承,即不能只是为了复用代码而选择继承
  • 这是因为继承的耦合度较高,会破坏系统的封装性,而组合和聚合的耦合度相对低
  • 也因为使用继承的地方就要考虑里氏替换原则,而简单的继承复用往往会打破LSP,反而增加系统的复杂度和维护难度

迪米特法则

又叫最少知识原则,有以下几种定义方式:

  • 不和陌生人说话
  • 只和直接朋友通信
  • 每个软件单位对其他单位都只有最少的知识,且局限于那些与本单位密切相关的软件单位

简单地说,一个软件实体应当尽可能少的与其他软件实体发生相互作用。这样一个模块发生修改时,会最少的影响其他模块,系统会更容易扩展。

迪米特法则限制了软件实体之间通信的宽度和深度。

对于一个对象,满足以下条件之一的对象就是朋友:

  1. 对象本身(this)
  2. 以参数形式传入当前对象方法中的对象
  3. 成员对象
  4. 如果成员对象是一个集合,集合元素也是朋友
  5. 当前对象创建的对象

狭义法则

如果两个类之间不必彼此直接通信,那么这两个类之间就不应该发生直接的相互作用。那么如果其中一个类需要调用另一个类的方法的话,通过第三者来转发请求,这个第三者和两个类都是朋友。

狭义法则降低了类耦合,但是会增加大量的小方法分散在系统各处。局部设计简化了但是系统不同模块之间的通信效率降低,不同模块之间不容易合作。

广义法则

对对象之间的信息流量、流向以及信息的影响的控制,主要是对信息隐藏的控制。

信息隐藏可以使系统之间解耦,不同系统可以被独立地开发、优化、使用和修改,方便进行模块复用。一个系统的规模越大,信息隐藏就越重要。

迪米特法则的作用:

  • 控制信息的过载
  • 尽量创建松耦合的类
  • 在类设计上,每个类都尽量降低其成员变量和成员函数的访问权限
  • 只要有可能,一个类应当被设计成不变类
  • 一个对象对其它对象的引用应当降到最低

总结

  • 目标:开闭原则
  • 指导:迪米特法则
  • 基础:单一职责原则、可变性封装原则
  • 实现:依赖倒转原则、合成复用原则、里氏代换原则、接口隔离原则