单一职责原则
单一职责原则:应该有且仅有一个原因引起类的变更,即一个类只负责一项职责。
遵循单一职责原的优点有:
- 可以降低类的复杂度,一个类只负责一项职责,其逻辑肯定要比负责多项职责简单的多;
- 提高类的可读性,提高系统的可维护性;
- 变更引起的风险降低,变更是必然的,如果单一职责原则遵守的好,当修改一个功能时,可以显著降低对其他功能的影响。
里氏替换原则
里氏替换原则:派生类(子类)对象能够替换其基类(超类)对象被使用,但不能改变基类的功能。
里氏替换原则具有以下含义:
- 子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法。
- 子类中可以增加自己特有的方法。
- 当子类的方法重载父类的方法时,方法的前置条件(即方法的形参)要比父类方法的输入参数更宽松。
- 当子类的方法实现父类的抽象方法时,方法的后置条件(即方法的返回值)要比父类更严格。
依赖倒置原则
依赖倒置原则:高层模块不应该依赖低层模块,二者都应该依赖其抽象; 抽象不应该依赖细节,细节应该依赖抽象。
相对于细节的多变性,抽象的东西要稳定的多。以抽象为基础搭建起来的架构比以细节为基础搭建起来的架构要稳定的多。
依赖倒置原则的核心思想是面向接口编程。
示例:妈妈给孩子讲故事。
场景一:只要给妈妈一本书,妈妈就可以给孩子讲故事了。
class Book{
public String getContent(){
return "在那遥远的山的那边海的那边..."
}
}
class Mother{
public void narrate(Book book){
System.out.println("妈妈开始讲故事");
System.out.println(book.getContent());
}
}
在需要的地方
(new Mother()).narrate(new Book());
就可以实现该场景了。
场景二:突然有一天,孩子需要妈妈给他将一份报纸上的故事,这个时候妈妈不会将了。
报纸的内容如下:
class Newspaper{
public String getContent(){
return "林书豪38+7领导尼克斯击败湖人……";
}
}
这太荒唐了,只是将书换成报纸,居然必须要修改Mother才能读。假如以后需求换成杂志呢?换成网页呢?还要不断地修改Mother,这显然不是好的设计。原因就是Mother与Book之间的耦合性太高了,必须降低他们之间的耦合度才行。
我们引入一个抽象的接口IReader。读物,只要是带字的都属于读物:
interface IReader{
public String getContent();
}
Mother类与接口IReader发生依赖关系,而Book和Newspaper都属于读物的范畴,他们各自都去实现IReader接口,这样就符合依赖倒置原则了,代码修改为:
class Newspaper implements IReader {
public String getContent(){
return "林书豪17+9助尼克斯击败老鹰……";
}
}
class Book implements IReader{
public String getContent(){
return "很久很久以前有一个阿拉伯的故事……";
}
}
class Mother{
public void narrate(IReader reader){
System.out.println("妈妈开始讲故事");
System.out.println(reader.getContent());
}
}
public class Client{
public static void main(String[] args){
Mother mother = new Mother();
mother.narrate(new Book());
mother.narrate(new Newspaper());
}
}
采用依赖倒置原则给多人并行开发带来了极大的便利,比如上例中,原本Mother类与Book类直接耦合时,Mother类必须等Book类编码完成后才可以进行编码,因为Mother类依赖于Book类。修改后的程序则可以同时开工,互不影响,因为Mother与Book类一点关系也没有。参与协作开发的人越多、项目越庞大,采用依赖导致原则的意义就越重大。现在很流行的TDD开发模式就是依赖倒置原则最成功的应用。
TDD开发模式:Test Driven Development,测试驱动开发。
接口隔离原则
接口隔离原则:类间的依赖关系应该建立在最小的接口上。(客户端不应该依赖其不需要的接口)。
与单一职责的区别:
- 单一职责原则注重的是职责;而接口隔离原则注重对接口依赖的隔离。
- 单一职责原则主要是约束类,其次才是接口和方法,它针对的是程序中的实现和细节;而接口隔离原则主要约束接口接口,主要针对抽象,针对程序整体框架的构建。
接口隔离原则注意点:
- 接口尽量小,但要适度。对接口进行细化可以提高程序设计灵活性,但是如果过小,则会造成接口数量过多,使设计复杂化。所以一定要适度。
- 提高内聚,减少对外交互。使接口用最少的方法去完成最多的事情。
- 为依赖接口的类定制服务。只暴露给调用的类它需要的方法,它不需要的方法则隐藏起来。只有专注地为一个模块提供定制服务,才能建立最小的依赖关系。
迪米特法则
迪米特法则(最少知道原则):一个对象应该对其他对象保持最少的了解。即类间解耦。
迪米特法则的初衷是降低类之间的耦合,由于每个类都减少了不必要的依赖,因此的确可以降低耦合关系。但是凡事都有度,虽然可以避免与非直接的类通信,但是要通信,必然会通过一个“中介”来发生联系,过分的使用迪米特原则,会产生大量的中介和传递类,导致系统复杂度变大。所以在采用迪米特法则时要反复权衡,既做到结构清晰,又要高内聚低耦合。
开放封闭原则
开放封闭原则(开闭原则):一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。
当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化。
一句话概括:
- 单一职责原则 ---- 实现类要职责单一;
- 里氏替换原则 ---- 不要破坏继承体系;
- 依赖倒置原则 ---- 要面向接口编程;
- 接口隔离原则 ---- 在设计接口的时候要精简单一;
- 迪米特法则 ---- 要降低耦合。
- 开闭原则是总纲 ---- 要对扩展开放,对修改关闭。
单一职责原则(Single Responsibility Principle,简称SRP )
里氏替换原则(Liskov Substitution Principle,简称LSP)
依赖倒置原则(Dependence Inversion Principle,简称DIP)
接口隔离原则(Interface Segregation Principle,简称ISP)
迪米特法则(Law of Demeter,简称LoD)
开放封闭原则(Open Close Principle,简称OCP)