1. 单一职责原则
就一个类而言,应该只有一个引起它变化的原因
1.1. 实例
1.1.1. 原有方案
存在的问题:在这个类中,前两个方法操作数据库,后两个方法操作图表
- 如果其他类也需要用到findCustomers()的功能,仍然需要在该类中重新实现,无法实现代码的复用
- 如果数据库相关功能或者图表相关功能需要修改,都需要直接修改这个类,有两方面的原因引起类的修改,不符合只有一个引起变化的原因,违反了单一职责原则
1.1.2. 解决方案
将CustomerDataChart进行拆分:
- DBUtil 负责数据库的连接工作
- CustomerDAO 负责从数据库中查询顾客信息
- CustomerDataChart 只负责图表相关的操作
修改后,只有图表相关的修改才会引起CustomerDataChart类的修改,也就是只有一个原因能引起它的变化,满足了单一职责原则
2. 开闭原则
一个软件实体应当对扩展开放,对修改关闭。即应该在尽量不修改原有代码的情况下进行扩展
3. 里氏代换原则
所有引用基类(父类)的地方必须能够透明地使用其子类(被引用的那个父类的子类)的对象
如果在软件中,将一个基类对象替换为其子类对象,不会产生错误或异常。而反之则不成立,如果在程序中使用的是子类对象,那么不一定能够用其基类对象进行替代
3.1. 实践
在程序设计时,应当将父类设计为抽象类或者接口,用子类对象实现父类中的方法,那么在运行时,就可以用子类对父类进行替换,而不影响程序功能。从而可以方便地扩展系统的功能,当需要扩展时,只需要写一个新的子类来实现父类方法即可。
4. 依赖倒转原则
抽象不应该依赖细节,细节应该依赖抽象。应该针对接口编程,而不是针对实现编程。
4.1. 综合实例
4.1.1. 原有方案
执行addCustomers方法时,需要对TXT格式或者Excel格式的数据源文件进行转换,而每次执行的时候只能选择其中的一种转换方式,若要修改转换方式,那么只能通过更改实现类或者新增实现类的方式进行修改,而直接修改代码的方式并不符合开闭原则。
4.1.2. 解决方案
- 引入抽象数据转换类DataConvertor,CustomerDAO针对抽象类进行编程,而具体的数据转换类名则由配置文件config.xml给出,符合依赖倒转原则
- 程序运行时,可以用抽象数据转换类的具体实现子类进行替换,而不产生错误或异常,符合里氏代换原则
- 当需要修改具体数据转换类时,只需要修改配置文件;当需要新增具体数据转换类的时候,只需要将新的类作为抽象类的子类进行实现,并且修改配置文件即可。无需修改CustomerDAO的代码,符合开闭原则
5. 接口隔离原则
使用多个专门的接口,而不使用单一的总接口,即客户端不该依赖于那些不需要的借口。
当一个接口太大时,应该将其拆分为多个小接口,而客户端只需要知道那些要用到的接口即可。
5.1. 接口的两种含义
- 逻辑上的概念
这里的接口表示一个类型所提供的所有方法或特征的集合,接口的划分直接带来角色的划分。
- 语言上的接口
如Java中的interface
这里的接口应当仅仅提供客户端所需要的行为。在这种情况下,写一个大而全的接口就并不合适了。因为当客户端实现一个接口的时候,需要实现接口内提供的所有方法,而其中有些方法是这个客户端所不需要的。
所以应该将大接口按照不同职责划分为小接口,客户端实现需要用到的接口即可。
5.2. 实例
5.2.1. 原有方案
ConcreteClass作为客户端,实现了CustomerDataDisplay接口,其中transformToXML方法可以将数据文件转换为XML格式
存在的问题:
- 当客户端无需将数据转换为XML格式时,仍然需要实现transformToXML方法
- 当客户端只需要生成图表的时候,仍然需要实现Report相关的方法
5.2.2. 解决方案
将接口进行拆分,拆分的时候要控制好拆分的粒度,既不要过小也不要过大,过小则导致系统接口泛滥,不易于维护,过大则违背了接口隔离原则,不够灵活。
拆分的大致原则是:一个接口仅需要包含为某一类用户定制的方法即可。
6. 合成复用原则
尽量适用对象组合,而不是继承的方式,来达到复用的目的
在建立新对象的时候,应该尽量用关联关系(组合关系、聚合关系)来使用已有的对象,使已有的对象成为新对象的一部分,从而可以使用旧对象的功能而达成复用。
6.1. 实例
6.1.1. 原有方案
CustomerDAO类通过继承DBUtil类的方式,实现对连接数据库的方法getConnection的调用
存在的问题:
- 若要将数据库更改为Oracle数据库,原有的getConnection方法便无法使用,要想使用新的连接方法,就必须修改CustomerDAO类的继承关系,继承OracleDBUtil类,或者修改DBUtil的代码,这两种方法均违反了开闭原则
6.1.2. 解决方案
使用关联关系重构代码
初始时可以将DBUtil注入CustomerDAO的util属性,通过属性对象调用方法连接数据库
当数据库改变时,创建OracleDBUtil类,继承DBUtil类,并重写getConnection方法,想要连接新的数据库时,根据里氏代换原则,只需要将DBUtil的子类OracleDBUtil类注入CustomerDAO的util属性,仍然调用getConnection方法即可连接新的数据库,无需更改CustomerDAO的代码,只需要改变注入就可改变数据库。
7. 迪米特法则
一个软件实体应当尽量减少与其他实体之间的相互作用(低耦合)
7.1. 实例
7.1.1. 原有方案
当触发Button时,按照交互关系链条,会触发所有的其他模块,这是一个高耦合的系统
7.1.2. 解决方案
通过定义中间件的形式,避免了组件之间的直接交互。组件之间的交互逻辑统一在中间件中实现。当需要新增或删除组件的时候,只需要修改中间件的代码即可,无需修改其他组件的源代码
本文参考刘伟老师的《设计模式的艺术》一书,感谢老师的付出,如有侵权,请联系我立刻删除