设计模式
一、单一职责原则
Single Responsibility Principle -- SRP 。单一职责原则 There should never be more than one reason for a class to change.
单一职责原则的好处:
- 类的复杂度降低,实现什么职责都有清晰明确的定义;
- 可读性提高
- 可维护性提高
- 变更引起的风险降低
关键在于职责的划分,例如在设计类和接口,一个点就是要区分实体的属性和行为,使类和接口在阅读和调用的时候更单纯。
对于单一职责原则,接口一定要做到单一职责,类的设计尽量做到只有一个原因引起变化。
// 电话过程 public interface IPhone { // 拨通电话 public void dial(String phoneNumber); // 通话 public void chat(Object o); // 通话完毕,挂电话 public void hangup(); }
上例中明显不满足单一职责要求,因为它包含了两个职责:一个是协议管理、一个是数据传送。重新规划如下:
二、里式替换原则
关于继承的优缺点
优点:
- 代码共享,减少了创建类的工作量,每个子类都拥有父类权限允许下的方法和属性
- 提高代码的重用性
- 提高了代码的可扩展性,重写父类方法
- 提高了产品和项目的开放性
缺点:
- 继承是侵入性的。只要是继承,就必须拥有父类的所有属性和方法
- 降低代码的灵活性
- 增强了耦合性
里式替换原则:Liskov Substitution Principle。所有引用基类的地方必须能透明地使用其子类的对象。Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it.
子类必须完全实现父类的方法
注意:如果子类不能完整的实现父类的方法,或者父类的某些方法在子类中已经发生畸变,则建议断开父子继承关系,采用依赖、聚合、组合等关系代替继承。
以玩具枪杀敌的结构图为例:
初始继承关系如下:
脱离后的继承关系设计:
子类可以有自己的个性
子类可以有自己的方法和属性
重写或实现父类的方法时输入参数可以被放大
重写或实现父类方法时,当父类的前置条件大于子类时,在实际调用中将出现执行子类而未执行父类,不符合里式替换原则。所以,子类中方法的前置条件必须与超类中被覆写的方法的前置条件相同或者更宽松。
其实就是重载与重写的正反向关联。
public class TestClient { public static void invoker(){ Father f = new Son(); HashMap map = new HashMap();
// 此处调用父类的方法 f.doSomething(map); } public static void main(String[] args) { invoker(); } } class Father { public Collection doSomething(HashMap map){ System.out.println("fater---do--"); return map.values(); } } class Son extends Father { public Collection doSomething(Map map){ System.out.println("son-----do--"); return map.values(); } }
重写或实现父类的方法时输出结果可以被缩小
同上。
采用里式替换原则,需尽量避免子类的“个性”,不推荐
三、依赖倒置原则 *
Dependence Inversion Principle,DIP。即 面向接口设计。OOD Object-Oriented Design
English:High level modules should not depend upon low level modules.Both should depend upon abstractions.Abstractions should not depend upon details.Details should depend upon abstractions.
Java:
- 模块间的依赖通过抽象发生,实现类之间不发生直接的依赖关系,其依赖关系是通过接口或抽象类产生的
- 接口或抽象类不依赖于实现类
- 实现类依赖接口或抽象类
司机驾驶奔驰车的类图:
引入依赖倒置原则后的类图:
依赖的三种写法:
- 构造函数传递依赖对象
- Setter方法传递依赖对象
- 接口声明依赖对象
ps:这里对照Spring的DI解读,依赖倒置原则是类和接口的设计原则,只是在依赖写法时推荐采用Spring的控制反转思想,以确保可读性和解耦合。
实际项目中的应用:
- 每个类尽量都有接口或抽象类,或者二者都具备。因为接口和抽象类都是属于抽象的,有了抽象才可能依赖倒置。
- 变量的表面类型尽量是接口或抽象类。
- 任何类都不应该从具体类派生。
- 尽量不要覆写基类的方法。
- 结合里式替换原则使用。
四、接口隔离原则
定义:Clients should not be forced to depend upon interfaces that they don't use.
The dependency of one class to another one should depend on the smallest possible interface.
接口隔离原则,用以保证接口的纯洁性,是对接口进行规范约束:
- 接口要尽量小 注:在拆分接口时,首先必须满足单一职责原则
- 接口要高内聚 高内聚就是提高接口、类、模块的处理能力,减少对外的交互
- 定制服务 单独为一个个体提供优良的服务
- 接口设计是有限度的 接口设计的粒度越小,系统越灵活,但也将增加开发难度和可维护性,需要把握好这个度
实践中的规则:
- 一个接口只服务于一个子模块或业务逻辑
- 通过业务逻辑压缩接口中的public方法
- 已经被污染了的接口,尽量去修改,若变更的风险较大,则采用适配器模式进行转化处理
- 了解环境,拒绝盲从
五、迪米特法则
Law of Demeter,LoD,即 Least Knowledge Principle。一个对象应该对其他对象有最少的了解。
迪米特法则对类的低耦合提出了明确的要求,其包含以下4层含义:
1. 只和朋友交流
2. 朋友间也是有距离的
类尽量减少public方法和非静态的public变量,多使用private、package-private、protected等访问权限
3. 是自己的就是自己的
如果一个方法放在本类中,既不增加类间关系,也对本类不产生负面影响,那就放置在本类中
4. 谨慎使用Serializable
主要在于类或接口在客户端和服务端交互中,同步更新中可能出现的问题。eg:在一个项目中使用RMI(Remote Method Invocation,远程方法调用)方式传递一个VO(Value Object,值对象),这个对象就必须实现S俄日阿里咋办了接口,也就是把需要网络传输的对象进行序列化,否则就会出现NotSerializableExceptiion异常。而一旦客户端的VO修改了一个属性的访问权限,从private变更为public,而服务器未作出相应变更,则会报序列化失败。
迪米特法则的核心观念就是类间解耦,弱耦合,只有弱耦合后,类的复用率才可以提高。其要求的结果就是产生了大量的中转或跳转类,导致系统的复杂性提高,同时也为维护带来难度。
具体的考量依托实际项目。
六、开闭原则
什么是开闭原则
Software entities like classes,modules and functions should be open for extension but closed for modifications.
软件实体应该对扩展开放,对修改关闭,其含义是说一个软件实体应该通过扩展来实现变化,而不是通过修改已有的代码来实现变化。
eg:针对已上线的项目新增功能,不能通过修改接口、修改实现类的方式来实现新功能,而是通过扩展实现变化,例如新建子类。
一个项目的基本路径应该是这样的:项目开发、重构、测试、投产、运维,其中的重构可以对原有的设计和代码进行修改,运维尽量减少对原有代码的修改,保持原始代码的纯洁性,提高系统的稳定性。
为什么采用开闭原则
1. 开闭原则对测试的影响
新增加的类,新增加的测试方法,只要保证新增加的类是正确的就可以了
2. 开闭原则可以提高复用性
在面向对象的设计中,所有的逻辑都是从原子逻辑组合而来的,而不是在一个类中独立实现一个业务逻辑。只有这样代码才可以复用,粒度越小,被复用的可能性就越大。
3. 开闭原则可以提高可维护性
4.面向对象开发的要求
如何使用开闭原则
1.抽象约束
书店新增业务领域:
要实现对扩展开发,首要的前提条件是抽象约束。
2. 元数据(metadata)控制模块行为
例如Spring的控制反转,业务变化只需实现一个Bean,完成注入即可。
3. 指定项目章程
4. 封装变化
对变化的封装包括两层含义:
1) 将相同的变化封装到一个接口或实现类中
2) 将不同的变化封装到不同的接口或抽象类中
设计模式的六大设计原则:
Single Responsibility Principle : 单一职责原则
Open Closed Principle : 开闭原则
Liskov Substitution Principle : 里式替换原则
Law of Demeter : 迪米特法则
Interface Segregation Principle : 接口抽离原则
Dependence Inversion Principle : 依赖倒置原则
首字母联合:SOLID,稳定的。
即建立稳定、灵活、健壮的设计。