设计模式6大设计原则

单一职责原则SRP(Single Responsibility Priciple)

所谓单一职责原则,指的是,一个类应该仅有一个引起它变化的原因。

这里变化的原因就是所说的“职责”,如果一个类有多个引起它变化的原因,那么也就意味着这个类有多个职责,再进一步说,就是把多个职责耦合在一起了。这会造成职责的相互影响,可能一个职责的变化,会影响到其他职责的实现,甚至引起其他职责随着变化。

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

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

里氏替换原则为良好的继承定义了一个规范,一句简单的定义包含4层含义:

  1. 子类必须完全实现父类的方法。

    • 在类中调用其他类时务必要使用父类或接口,如果不能使用父类或接口,则说明类的设计已经违背了LSP原则
    • 如果子类不能完整地实现父类的方法,或者父类的某些方法在子类中已经发生了”畸变”,则建议断开父子继承关系,采用依赖、聚集、组合等关系代替继承
  2. 子类可以有自己的个性

  3. 覆盖或实现父类的方法时输入参数可以被放大
  4. 覆写或实现父类的方法时输出结果可以被缩小

里式替换原则有一下两种含义:

  1. 里式替换原则是针对继承而言的,如果继承是为了实现代码重用,也就是为了共享方法,那么共享的父类方法就应该保持不变,不能被子类重新定义。子类只能通过新添加方法来扩展功能,父类和子类都可以实例化,而子类继承的方法和父类是一样的,父类调用方法的地方,子类也可以调用同一个继承得来的,逻辑和父类一致的方法,这时用子类对象将父类对象替换掉时,当然逻辑一致,相安无事。
  2. 如果继承的目的是为了多态,而多态的前提就是子类覆盖并重新定义父类的方法,为了符合LSP,我们应该将父类定义为抽象类,并定义抽象方法,让子类重新定义这些方法,当父类是抽象类时,父类就是不能实例化,所以也不存在可实例化的父类对象在程序里。也就不存在子类替换父类实例时逻辑不一致的可能。

不符合LSP的最常见的情况是,父类和子类都是可实例化的非抽象类,且父类的方法被子类重新定义,这一类的实现继承会造成父类和子类间的强耦合,也就是实际上并不相关的属性和方法牵强附会在一起,不利于程序扩展和维护。

总结一句话——就是尽量不要从可实例化的父类中继承,而是要使用基于抽象类和接口的继承。

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

包含三层含义:

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

在Java语言中,抽象就是指接口或抽象类,两者都是不能直接被实例化的;细节就是实现类,实现接口或继承抽象类而产生的类就是细节,其特点就是可以直接被实例化。依赖倒置原则在Java语言中的表现就是:

  • 模块间的依赖通过抽象发生,实现类之间不发生直接的依赖关系,其依赖是通过接口或抽象类产生的
  • 接口或抽象类不依赖于实现类
  • 实现类依赖接口或抽象类

更加精简的定义就是”面向接口编程”——OOD(Object-Oriented Design,面向对象设计)的精髓之一

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

接口隔离原则是对接口进行规范约束,其包含一下4层:

  • 接口要尽量小

    这是接口隔离原则的核心定义,不出现臃肿的接口,但是”小”是有限度的,首先就是不能违反单一职责原则。

  • 接口要高内聚

    高内聚就是提高接口、类、模块的处理能力,减少对外的交互。

  • 定制服务

  • 接口的设计是有限度的

    接口的设计粒度越小,系统越灵活。但是灵活的同时也带来了结构的复杂化,开发难度增加,可维护性降低,所以接口设计一定要注意适度。

迪米特法则LOD(Law of Demeter)

迪米特法则(Law of Demeter, LoD)也称为最少知识原则(Least Knowledge Principle, LKP),描述的是同一个规则:一个对象应该对其他对象有最少的了解。通俗地讲,一个类应该对自己需要耦合或调用的类知道得最少。

迪米特法则对类的低耦合提出了明确的要求,其包含以下4层含义:

  1. 只和朋友交流

    朋友类的定义是这样的:出现在成员变量、方法的输入输出参数中的类称为成员朋友类,而出现在方法体内部的类不属于朋友类。类与类之间的关系是建立在类间的,而不是方法间,因此一个方法尽量不引入一个类中不存在的对象,当然,JDK API提供的类除外

  2. 朋友间也是有距离的

    尽量不要对外公布太多的public方法和非静态的public变量,尽量内敛,多使用private、package-private、protected等访问权限。

  3. 是自己的就是自己的

    如果一个方法放在本类中,即不增加类间关系,也对本类不产生负面影响,就放置在本类中

  4. 谨慎使用Serializable

开闭原则OCP(Open-Closed Priciple)

软件实体应该对扩展开放,对修改关闭,其含义是说一个软件实体应该通过扩展来实现变化,而不是通过修改已有的代码来实现变化。软件实体包括以下几个部分:

  • 项目或软件产品中按照一定的逻辑规划划分的模块
  • 抽象和类
  • 方法

开闭原则对扩展开放,对修改关闭,并不意味着不做任何修改,底层模块的变更,必然要有高层模块进行耦合,否则就是一个孤立无意义的代码片段。