目录

一、单一职责原则

二、里氏替换原则

三、依赖倒转原则

四、接口隔离原则

五、迪米特法则

六、开闭原则


一、单一职责原则

单一职责原则的英文名称是:Single Responsibility Principle,简称SRP。它的内容是:应该有且只有一个引起类变化的原因

例如下面这个类,它的设计就违反了单一职责原则:

经过职责划分后的类图如下:

重新拆封成两个接口,IUserBO负责用户的属性,简单地说,IUserBO的职责就是收集和反馈用户的属性信息;IUserBiz负责用户的行为,完成用户信息的维护和变更。

分清职责后的代码示例:

......
IUserInfo userInfo = new UserInfo();
//我要赋值了,我就认为它是一个纯粹的BO
IUserBO userBO = (IUserBO)userInfo;
userBO.setPassword("abc");
//我要执行动作了,我就认为是一个业务逻辑类
IUserBiz userBiz = (IUserBiz)userInfo;
userBiz.deleteUser();
......

采用SRP的类图如下:

单一职责原则的好处:

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

单一职责适用于接口、类,同时也适用于方法。

二、里氏替换原则

里式替换原则的英文名称是:Liskov Substitution Principle,简称LSP。它的内容是:所有引用基类的地方必须能透明地使用其子类的对象。通俗地讲,只要父类能出现的地方子类就可以出现,而且替换为子类也不会产生任何错误或异常。

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

(1)子类必须完全实现父类的方法

例如下面这个例子,模拟射击游戏的枪支类:

设计一个枪支抽象类,然后具体的枪支继承这个抽象类,并实现对应的shoot方法。

public abstract class AbstractGun {public abstract void shoot();
} 
public class HandGun extends AbstractGun {@Overridepublic void shoot() {System.out.println("手枪设计...");}
}//Rifle、MachineGun省略

在士兵类中,使用枪来杀敌,但是这个枪是抽象的,具体是什么类型的枪需要通过setGun方法来确定。

public class Soldier {private AbstractGun gun;public void setGun(AbstractGun gun) {this.gun = gun;}public void killEnemy() {System.out.println("士兵开始杀敌人...");gun.shoot();}
}
public class Client {public static void main(String[] args) {Soldier soldier = new Soldier();soldier.setGun(new HandGun());soldier.killEnemy();}
}

在类中调用其他类时务必使用父类或者接口,如果不能使用父类或者接口,则说明类的设计已经违背了LSP原则。 

        下面定义一个玩具枪类:

如果还是使用原来的继承方式,由于玩具枪是不能射击的,所以此时的shoot方法就“没用”了。这就导致了正常的业务逻辑不能运行了。此时,有两种解决方法:

1)在Soldier类中增加instanceof的判断,如果是玩具枪就不能用来杀敌人。但是,这种修改每增加一个类就必须修改,所以不提倡。

2)将ToyGun脱离继承,建立一个独立的父类,并与AbstractGun建立关联委托关系。

如果子类不能完整地实现父类的方法,或者父类的某些方法在子类中已经发生“畸变”,则建议断开父子继承关系,采用依赖、聚集、组合等关系代替继承。 

(2)子类可以有自己的个性

        还是上面的例子,假设增加AUG狙击枪和狙击手类,此时除了实现shoot方法之外,还可以增加一些自己特有的方法。

public class AUG extends Rifle {public void shoot() {System.out.println("AUG射击...");}public void zoomOut() {System.out.println("通过瞄准镜观察敌人...");}
}
public class Snipper {private AUG aug;public void setGun(AUG aug) {this.aug = aug;}public void killEnemy() {aug.zoomOut();aug.shoot();}
}

(3)重写或者实现父类的方法时输入参数可以被放大      

当我们重载父类中的方法的时候(注意:重载(Overload)父类的方法而不是重写(Override)),必须要满足子类中方法的前置条件(参数)必须与超类中被重载的方法的前置条件相同或者更宽松。

public class Father {public Collection doSomething(HashMap map) {System.out.println("父类被执行...");return map.values();}
}
public class Son extends Father {//放大输入参数类型public Collection doSomething(Map map) {System.out.println("子类被执行...");return map.values();}
}

最终的结果和使用父类时的打印结果是一样的。

public class Client {public static void invoker() {//父类存在的地方,子类就应该存在//Father f = new Father();Son f = new Son();HashMap map = new HashMap();f.doSomething(map);}    public static void mian(String[] args) {invoker();}
}//结果:父类被执行...

        除非重写父类中的方法,否则,子类代替父类传递到调用者中,子类的方法永远不会执行。

(4)重写或者实现父类的方法时输出结果可以被缩小

同理,当我们重载父类中的方法的时候,必须要满足子类中方法的后置条件(返回值)必须与超类中被重载的方法的后置条件相同或者更狭窄。

        总而言之,采用里式替换原则的目的就是增强程序的健壮性,版本升级时也可以保持非常好的兼容性即使增加子类,原有的子类还可以继续运行。

三、依赖倒转原则

依赖倒转原则的英文名称是:Dependence Inversion Principle,简称DIP。它主要有三层含义:

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

依赖倒转原则在Java语言中的表现就是:

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

对象的依赖关系有三种写法来传递:

(1)构造函数传递依赖对象

(2)Setter方法传递依赖对象

(3)接口声明依赖对象

如下面这个例子,按照下面这种设计就不符合依赖倒转原则,具体的汽车类应该依赖一个抽象类。

经过修改后的类图如下:

四、接口隔离原则

接口隔离原则定义如下:

(1)客户端不应该依赖它不需要的接口;

(2)类间的依赖关系应该建立在最小的接口上。

        概括为一句话为:建立单一接口,不要建立臃肿庞大的接口。同时,接口尽量细化,同时接口中的方法尽量少。

        例如下面这个例子,定义了一个IPetteyGirl接口,声明所有的PetteyGirl的标准是goodLooking、niceFigure和greatTemperament,然而实际的情况并不是这样,显然这个接口设计的过于庞大了。

修改后的类图如下:

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

  • 单一职责原则要求的是类和接口的职责要一致
  • 接口隔离原则要求接口中的方法尽量少

接口隔离原则对接口进行规范约束,包含了以下4层含义:

(1)接口要尽量小

但是“小”是有限度的,首先必须满足单一职责原则

(2)接口要高内聚

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

(3)定制服务

定制服务就是单独为一个个体提供优良的服务,因此在接口设计时要求:只提供访问者需要的方法。

(4)接口的设计是有限度的

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

五、迪米特法则

迪米特法则的英文名称是:Law of Demeter,简称LoD,也称最少知识原则。它的规则是:一个对象应该对其他的对象有最少的了解。通俗的讲,一个类应该对自己需要耦合或调用的类知道得最少。

        如下面这个例子:老师希望体育委员统计女生的数量,在这个场景下,显然老师不需要与女生产生依赖关系,只需要与体育委员依赖即可。而在原来的类的设计中,老师和体育委员与女生同时存在依赖关系,这显然不符合迪米特法则。

public class Teacher {public void commond(GroupLeader groupLeader) {List<Girl> listGirls = new ArrayList();//初始化女生for(int i = 0; i < 20; i++) {listGirls.add(new Girl());}groupLeader.countGirls(listGirls);}
}
public class GroupLeader {public void countGirls(List<Girl> listGirls) {System.out.println("女生数量是:" + listGirls.size());}
}

根据迪米特法则进行如下修改:

public class Teacher {public void commond(GroupLeader) {groupLeader.countGirls(listGirls);}
}
public class GroupLeader {private List<Girl> listGirls;public GroupLeader(List<Girl> listGirls) {this.listGirls = listGirls;}public void countGirls() {System.out.println("女生数量是:" + listGirls.size());}
}

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

(1)“只和朋友交流”

如果两个对象耦合,那么它们就是“朋友”关系。一个类只和朋友交流,不与陌生类交流。不要出现getA().getB()这种情况,类与类之间的关系是建立在类间的,而不是方法间。因此,一个方法尽量不引入一个类中不存在的对象(JDK API提供的方法除外)。

(2)“朋友间也是有距离的”

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

(3)“是自己的就是自己的”

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

(4)谨慎使用Serializable

六、开闭原则

开闭原则是六大设计原则中最基础的设计原则。它的定义是:一个实体(如类、模块或者函数)的设计应该对扩展开放,对修改关闭。

开闭原则的核心思想面向抽象编程。通过接口或抽象类来约束一组可能变化的行为,从而实现对扩展开放,任何时候我们都不会直接去修改原来类中的内容,从而实现了对修改关闭。

如下面这个例子,如果我们希望再想实现图书“打折”的效果,我们不应该直接去增加一个setPrice的方法或者再原有的getPrice方法中进行修改,正确的做法是:创建一个打折类,并且这个类继承自NovelBook类,通过重写打折类中的getPrice方法来实现。

public class OffNovelBook extends NovelBook{public OffNovelBook(String name,int price,String author){super(name,price,author);}//覆写价格方法,当价格大于40,就打8析,其他价格就打9析public int getPrice(){if(this.price > 40){return this.price * 0.8;}else{return this.price * 0.9;}     }
}

开闭原则包含三层含义:

(1)通过接口或抽象类来约束扩展,对扩展进行边界限定,不允许出现在接口或者抽象类中不存在的public方法;

(2)参数类型、引用类型尽量使用接口或者抽象类,而不是实现类;

(3)抽象类尽量保持稳定,一旦确定即不允许修改。

        开闭原则的重要性:

  • 面向对象的要求
  • 提高代码的复用性
  • 提高代码的可维护性
  • 便于进行单元测试

“设计模式之禅”——六大设计原则详解解读相关推荐

  1. 设计模式之禅(六大设计原则)

    1.单一职责原则(Single Responsibility Principle) 也就是职责划分要明确,单一职责原则提出了一个编写程序的标准,用"职责"或者"变化原因& ...

  2. 引用防删——JAVA设计模式总结之六大设计原则

    JAVA设计模式总结之六大设计原则 从今年的七月份开始学习设计模式到9月底,设计模式全部学完了,在学习期间,总共过了两篇:第一篇看完设计模式后,感觉只是脑子里面有印象但无法言语.于是决定在看一篇,到9 ...

  3. java设计模式总结之六大设计原则(有图有例子)

    转载:https://www.cnblogs.com/jpfss/p/9765239.html 下面来总结下自己所学习的设计模式,首先我们看下各个模式之间的关系图,下面这张图是网上比较典型的一个类图关 ...

  4. JAVA设计模式总结之六大设计原则(一)

    从今年的七月份开始学习设计模式到9月底,设计模式全部学完了,在学习期间,总共过了两篇:第一篇看完设计模式后,感觉只是脑子里面有印象但无法言语.于是决定在看一篇,到9月份第二篇设计模式总于看完了,这一篇 ...

  5. 设计模式系列,六大设计原则

    设计模式和性能优化有没有关系?最近,我看到有人再讲性能优化的时候,讲到了"有些设计模式可以做到一定程度的性能优化". 我读书少,别骗我.我看过无数篇设计模式了,第一次听到有人说,设 ...

  6. 举例说明层次分析的三大原则_设计模式系列,六大设计原则

    设计模式和性能优化有没有关系?最近,我看到有人再讲性能优化的时候,讲到了"有些设计模式可以做到一定程度的性能优化". 我读书少,别骗我.我看过无数篇设计模式了,第一次听到有人说,设 ...

  7. 设计模式必备知识点----六大设计原则

    六大设计原则 一,开闭原则 开闭原则的定义 什么是开闭原则 开闭原则的作用 开闭原则的优点 二,单一职责原则 单一职责定义 单一职责的作用 单一职责的优点 单一职责的违背原则 三,依赖倒置原则 依赖倒 ...

  8. Java 设计模式总结及六大设计原则

    设计模式总结 总体来说设计模式分为三大类: 创建型模式,共五种:工厂方法模式.抽象工厂模式.单例模式.建造者模式.原型模式. 结构型模式,共七种:适配器模式.装饰器模式.代理模式.外观模式.桥接模式. ...

  9. Java设计模式中的六大设计原则

    最近一直在看有关设计模式的博客和文章,发现自己对于设计模式的认识和理解还是有点浅显,于是想动手写博客巩固一下. 在开始阐述设计模式之前,首先介绍一下设计模式中的六大原则:      总原则-开闭原则 ...

最新文章

  1. C语言输入3298运行结果为,浙江农林大C语言程序设计习题集答.doc
  2. LeetCode: 150:逆波兰表示法求值。
  3. 【SSH高速进阶】——struts2简单的实例
  4. Android 8.0 学习(14)---Android8.0适配分析
  5. 模式代码 java中aes_深入浅出:Java中的代理模式
  6. 平板直撑的腰椎问题(塌腰)
  7. 安装包制作工具 SetupFactory使用 详解
  8. 图像拼接算法总结(一)
  9. 完美解决VS2003.Net fatal error LNK1201: 写入程序数据库“.pdb”时出错我的开发环境是Win7旗舰64位+VS2003.Net,经常卡pdb错误,文末给出一个完美的解决
  10. 圆形头像制作,仿QQ做法。
  11. Team Foundation Server 开发流程管理管理研讨会
  12. Python 一键导出微信读书的书籍和笔记
  13. 报错:Torch not compiled with CUDA enabled看这一篇就足够了
  14. SQLServer触发器的使用
  15. 厚积薄发 | 游戏引擎十年技术点滴
  16. 我的世界1.12.2 神奇宝贝(精灵宝可梦) 开服教程
  17. 架构师进阶之路——1、持久化框架(一)
  18. React-18--css in js
  19. Material studio 2017R2生成的模型文件导入WIN10中lammps遇到的小问题
  20. 【C语言】练习:给出三角形三边长,求三角形面积

热门文章

  1. What is Wine
  2. (2019年10月更新) Android 最全的底部导航栏实现方法
  3. 小白Ubuntu安装ROS详细教程及常见问题分析
  4. 如何用雷达触控系统代替油墨导电系统?
  5. matlab adpcm编码,求助 求G.721 ADPCM语音编码
  6. Python中使用matplotlib绘制雷达图
  7. 川菜馆|文科生的python挑战(1)——字符串
  8. Java计算机毕业设计电动机营销系统源码+系统+数据库+lw文档
  9. sql个人学习笔记记录
  10. PHP中SQL注入与跨站攻击的防范