一.单一职责原则
定义:
一个接口,类或者方法只有一个原因引起变化.即只有一个职责.
解读:
职能多样是一个诱惑,一个方法如果能根据入参(可变参数等)或其他判断条件实现不同或多种功能,看起来很强大而且很酷,但这也意味着里面的一个分支功能改变时,整个方法都要受影响.
难点:
划分职责(可变因素)
注意:
职责是业务逻辑上的划分,类或接口单一职责并不代表单一方法
单一职责是一种理想状态,实际开发中很难完全贯彻,因为可能人为增加设计的复杂性,应结合实际情况.
建议:
接口一定要做到单一职责,类的设计则尽量做到只有一个原因引起变化
例子:
筷子不符合单一职责原则,刀叉则符合单一职责原则
二.里氏替换原则
定义:
所有引用基类的地方必须能透明地使用其子类的对象
解读:
所有父类出现的地方都可以用子类替换且不产生任何错误或异常,使用者不必知道是使用父类还是子类,反之,不一定成立.
子类必须完全实现父类的方法(子类可以替换父类)
子类可以有自己的个性(父类不一定可以替换子类)
覆盖或实现父类的方法是输入参数可以被放大(子类方法可以代替父类方法接收外界参数)
覆盖或实现父类的方法时输出结果可以被缩小(能接收父类方法输出结果的一定能接收子类方法输出结果)
例子:
1.service接口和serviceImpl实现类,调用时用service接收serviceImpl对象
2.子类实现的父类的方法其异常抛出范围不能越过父类方法定义的异常抛出范围
三.依赖倒置原则
定义:
面向接口编程
解读:
倒置是相对正置而言的,依赖正置即依赖于实体,是普通的依赖关系,需要什么就依赖什么.
依赖倒置是依赖于抽象,即依赖于某种契约,依赖什么就会有什么.
要求:
模块间依赖通过抽象(接口或抽象类)产生,实现类之间不发生直接的依赖关系
抽象(接口或抽象类)不依赖于实体(实现类)
实体(实现类)依赖于抽象(接口或抽象)
实践:
每个类都尽量有接口或抽象类,或同时有之(依赖倒置的基本前提)
变量的表名类型尽量是接口或抽象类
任何类都应该尽量避免从具体类中派生(开发阶段如此,项目维护时很难做到)
实现类避免覆写基类已实现的方法(较少见,覆写有可能改变基类的"契约")
与里氏替换原则结合使用
例子:
service接口和serviceImpl实现类,使用时用service接收serviceImpl对象
四.接口隔离原则
定义:
类间的依赖关系应该建立在最小的接口上(不应该强迫客户端依赖于他们不会使用的接口)
解读:
接口指的是一种规范,即可以是Java语义下的接口(类接口)或类(实例接口)
最小的接口有两层含义:
第一,依赖的接口应该尽可能少,只含必要的接口.
第二,接口应该只含有必要的方法,即接口应该尽可能细化,接口包含的方法应该尽可能少.
简单的理解接口隔离就是将一个臃肿的接口拆分为多个独立的接口,通过组合依赖他们,按需依赖,实现
注意:
和单一职责的区别在于:
单一职责是相对于职责而言的,接口隔离是相对于依赖关系的
一个符合单一职责的接口可能有多个方法(如相同职责,提供给不同的外部模块),但这是接口隔离原则所不允许的(对于外部模块来说,外部模块只需要一个方法,却被迫依赖了其他不需要的方法).
一个接口一个方法是肯定满足接口隔离原则的,但可能不满足单一职责原则(可能把单一职责的方法都拆出来了).
设计是有限的,过度的拆分会使得结构复杂,开发难度增加,可维护性降低.所以一般我们认为,接口隔离的前提是满足单一职责原则.
要求:
1.接口要尽可能小(但不能违反单一职责原则)
2.接口要高内聚(减少接口中public 的方法,内部使用的方法用private,减少对外承诺)
3.定制服务(如果一个模块只需要接口中的部分方法,那就应该定制一个只含有部分方法的接口给他,而不是偷懒全给)
4.接口设计是有限度的(不应该过度设计,应该考虑实际情况)
五.迪米特法则(最少知识原则)
定义:
一个对象应该对其他对象有最少的了解(一个类只和朋友类交流)
解读:
直接的朋友类:出现在成员变量,方法的输入输出参数中的类(通常是注入产生)
非直接朋友类(陌生类):出现在方法内部中的类(通常是动态生成)
一个类应该对自己需要耦合或调用的类知道得最少,即类间解耦,弱耦合.
要求:
1.只与直接的朋友通信
类与类之间的关系是建立在类间的,而不是方法间.即一个方法引用的对象应该是类的成员对象(通过输入参数完成初始化)
2.朋友间也是有距离的
减少public的方法和非静态的public属性,改用private,package-private,protected,考虑是否可以加上final关键字
注意:
迪米特法则的核心是类间解耦,其代价是产生大量的中转类(如成员变量类),实际应用中一个类对另一个类的访问不应该跳转超过两次,完全解耦是不可能的,具体应视实际情况而定.
六.开闭原则
定义:
一个软件实体(如类,模块和方法)应该对扩展开放,对修改关闭
解读:
对修改关闭并不意味着不对原模块(高层模块)做任何修改,低层模块的修改(即新增扩展)必然要有高层模块进行耦合,否则扩展孤立无效.
地位:
开闭原则是对其他五大原则的抽象,其他五大原则是对开闭原则的实现
要求:
1.抽象约束
通过尽可能稳定的抽象层约束可能变化的行为,以实现对扩展开放
例子:
依赖倒置原则
2.元数据(配置参数)控制模块行为
通过修改配置文件来完成业务变化
例子:
Spring的IOC控制反转
3.制定项目章程
约定优于配置
例子:
SpringBoot
4.封装变化
有两层含义:
1.将相同的变化封装到一个接口或抽象类中
2.将不同的变化封装到不同的接口或抽象类中(不应该有两个不同的变化出现在同一个接口或抽象类中)
即通过为变化点创建稳定的接口,从而封装可能发生的变化
例子:
23种设计模式