一文读懂面向对象设计的七大原则

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. 接口的两种含义

  1. 逻辑上的概念

这里的接口表示一个类型所提供的所有方法或特征的集合,接口的划分直接带来角色的划分。

  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. 解决方案

通过定义中间件的形式,避免了组件之间的直接交互。组件之间的交互逻辑统一在中间件中实现。当需要新增或删除组件的时候,只需要修改中间件的代码即可,无需修改其他组件的源代码

本文参考刘伟老师的《设计模式的艺术》一书,感谢老师的付出,如有侵权,请联系我立刻删除

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值