面向对象设计六大原则


一、单一职责原则

单一职责原则的英文名称是Single Responsibility Principle,简称SRP,原始定义如下:

There should never be more than one reason for a class to change。

在面向对象编程领域中,单一职责原则(Single responsibility principle)规定每个类都应该有一个单一的职责或者叫功能,并且该功能应该由这个类完全封装起来。所有它的(这个类的)服务都应该严密的和该功能平行(功能平行,意味着没有依赖)。一个类或者模块应该有且只有一个改变的原因

想象有一个用于编辑和打印报表的模块。这样的一个模块存在两个改变的原因。第一,报表的内容可以改变(编辑)。第二,报表的格式可以改变(打印)。这两方面会的改变因为完全不同的起因而发生:一个是本质的修改,一个是表面的修改。单一职责原则认为这两方面的问题事实上是两个分离的功能,因此他们应该分离在不同的类或者模块里。把有不同的改变原因的事物耦合在一起的设计是糟糕的。保持一个类专注于单一功能点上的一个重要的原因是,它会使得类更加的健壮。

单一职责的好处:

  1. 类的复杂性降低,实现什么职责都有清晰明确的定义;
  2. 可读性提高,复杂性降低,可维护性提高;
  3. 变更引起的风险降低。

对于单一职责原则,在实际设计中,接口一定要做到单一职责,类的设计尽量做到只有一个原因引起变化

二、开闭原则

开闭原则英文名是:Open Closed Principle,简称OCP,原始定义如下:

software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification

开闭原则(OCP)是面向对象设计中“可复用设计”的基石,是面向对象设计中最重要的原则之一,其它很多的设计原则都是实现开闭原则的一种手段。核心就是:对扩展开放,对修改关闭

实现开闭原则的关键就在于“抽象”。把系统的所有可能的行为抽象成一个抽象底层,这个抽象底层规定出所有的具体实现必须提供的方法的特征。作为系统设计的抽象层,要预见所有可能的扩展,从而使得在任何扩展情况下,系统的抽象底层不需修改;同时,由于可以从抽象底层导出一个或多个新的具体实现,可以改变系统的行为,因此系统设计对扩展是开放的。在实际开发过程的设计开始阶段,就要罗列出来系统所有可能的行为,并把这些行为加入到抽象底层,根本就是不可能的,这么去做也是不经济的。因此我们应该现实的接受修改拥抱变化,使我们的代码可以对扩展开放,对修改关闭。

开闭原则的好处:

  1. 可复用性好;
  2. 可维护性好。

三、里氏替换原则

里氏替换原则的英文名是:Liskov Substitution principle,简称:LSP,原始定义如下:

Functions that use pointers of references to base classes must be able to use objects of derived classes without knowing it.(所有引用基类的地方必须能透明地使用其子类的对象)

更通俗的定义即为:子类可以扩展父类的功能,但不能改变父类原有的功能。里氏替换原则包含了一下4层含义:

  1. 子类必须完全实现父类的方法;
  2. 子类可以有自己打个性,增加自己特有的方法;
  3. 覆盖或实现父类的方法时输入参数可以被放大;
  4. 覆盖或实现父类的方法时输出结果可以被缩小。

在项目中,采用里氏替换原则时,尽量避免子类打“个性”,一旦子类有“个性”,这个子类和父类之间打关系就很难调和了,把子类当作父类使用,子类的“个性”被抹杀——委屈了点;把子类单独作为一个业务来使用,则会让代码间打耦合关系变得扑朔迷离——缺乏类替换的标准。

四、迪米特法则

迪米特法则(Law of Demeter,简写为: LoD)又叫作最少知识原则(Least Knowledge Principle,简写LKP),就是说一个对象应当对其他对象有尽可能少的了解。通俗的讲,一个类应该对自己需要耦合或调用的类知道得最少,被耦合的类是如何的复杂都和我没关系,即为“不和陌生人说话”。

迪米特法则还有一个英文解释:

talk only to your immediate friends.(只与直接的朋友通信)

迪米特法则的初衷在于降低类之间的耦合。由于每个类尽量减少对其他类的依赖,因此,很容易使得系统的功能模块功能独立,相互之间不存在(或很少有)依赖关系。

迪米特法则不希望类之间建立直接的联系。如果真的有需要建立联系,也希望能通过它的友元类来转达。因此,应用迪米特法则有可能造成的一个后果就是:系统中存在大量的中介类,这些类之所以存在完全是为了传递类之间的相互调用关系——这在一定程度上增加了系统的复杂度,同时也为系统的维护带来了难度。所以,在采用迪米特法则时需要反复权衡,不遵循不对,严格执行又会“过犹不及”。既要做到让结构清晰,又要做到高内聚低耦合

五、接口隔离原则

Clients should not be forced to depend upon interfaces that they do not use.(客户端只依赖于它所需要的接口;它需要什么接口就提供什么接口,把不需要的接口剔除掉。)

The dependency of one class to another one should depend on the smallest possible interface.(类间的依赖关系应建立在最小的接口上。)

即,接口尽量细化,接口中的方法尽量少。接口隔离原则与单一职责原则的审视角度是不同的,单一职责原则要求的是类和接口职责单一,注重的是职责,这是业务逻辑上的划分,而接口隔离原则要求接口的方法尽量少。根据接口隔离原则拆分接口时,首先必须满足单一职责原则。

采用接口隔离原则对接口进行约束时,要注意以下几点:

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

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

六、依赖倒置原则

依赖倒置原则(Dependency Inversion Principle,DIP)的定义内容是:

High-level modules should not depend on low-level modules. Both should depend on abstractions.(高层模块不应该依赖低层模块,两者都应该依赖其抽象)

Abstractions should not depend on details. Details should depend on abstractions.(抽象不应该依赖细节;细节应该依赖抽象)

面向过程的开发,上层调用下层,上层依赖于下层,当下层剧烈变动时上层也要跟着变动,这就会导致模块的复用性降低而且大大提高了开发的成本。 面向对象的开发很好的解决了这个问题,一般情况下抽象的变化概率很小,让用户程序依赖于抽象,实现的细节也依赖于抽象。即使实现细节不断变动,只要抽象不变,客户程序就不需要变化。这大大降低了客户程序与实现细节的耦合度。

依赖倒置原则基于这样一个事实:相对于细节的多变性,抽象的东西要稳定的多。以抽象为基础搭建起来的架构比以细节为基础搭建起来的架构要稳定的多。在Java中,抽象指的是接口或者抽象类,细节就是具体的实现类,使用接口或者抽象类的目的是制定好规范和契约,而不去涉及任何具体的操作,把展现细节的任务交给他们的实现类去完成。依赖倒置原则的核心思想就是面向接口编程



转载请注明:闪烁之狐 » 面向对象设计六大原则

分享到:
主题颜色面板