可维护性和可复用性
1.1. 可维护性(Maintainability)
如果要考虑到软件的可维护性, 从设计之初就要考虑软件的可扩展性 灵活性 可插拔性 等等;
1.2. 可复用性(Reuseability)
复用不仅仅是代码的复用: 代码的复用 算法的复用 数据结构的复用
要做到这两点, 在设计的时候, 就必须遵循一些原则, 我们称之为面向对象的设计原则
2. 设计原则 SOLIDCD
设计原则一览:(注: 这个缩写是我自己的记忆简写)
S => SRP=Single Responsibility Principle = 单一职责原则
O => OCP=Open Close Principle = 开闭原则
L => LSP= Liscov Substitution Principle = 里氏代换原则
I => ISP = Interface Segregation Principle = 接口隔离原则
D => DIP = Dependency Inversion Principle = 依赖倒转原则
C => CARP = Composite/Aggregation Reuse Principle = 合成/聚合复用原则
D => Lod=**Law of Demeter ** = 迪米特法则
1. 单一职责原则
Single Responsibility Principle
一个类/接口应该专注于做一件事情, 降低类/接口的复杂度, 一个类/接口只负责一项职责,其逻辑保持简单;
可读性高,可维护性高;变更导致的风险就低:
当修改一个功能时,可以显著降低对其他功能的影响
需要说明的一点是单一职责原则不只是面向对象编程思想所特有的,只要是模块化的程序设计,都适用单一职责原则。
2. 开闭原则
Open Close Principle
软件设计应对扩展开放, 对修改关闭
如何做到OCP? 抽象化是关键;
比如: 玉帝诏安美猴王; 梁山好汉被诏安; 就是典型的OCP:
宫廷不废一兵一卒, 不破坏原来的体系制度 ==> 对修改关闭!
只需要在原来的制度上新增几个小小的职位实例, 就可以完成改造 ==> 对扩展开放!
设计模式案例: 策略模式
对规则的引用, 依赖于一个策略的接口, 策略的实现是可定制的, 想要新增新的规则, 只需要实现一套新的策略, 替换之即可!
见: jdk中的设计模式-策略模式-RejectedExecutionHandler
3. 里氏替换原则
Liscov Substitution Principle
任何基类(父类)可以出现的地方, 子类定可替换之!
设计模式案例:
代理模式
代理模式: 代理类和被代理类实现同一个接口, 我们应用的上下文使用的是抽象接口, 这样的话, 代理类就可以替换原来的接口实现类, 代替它来完成操作;
策略模式
策略模式: 策略接口是一个抽象的接口, 策略的实现是N个不同的策略实现类, 我们应用的上下文使用的是策略的抽象接口, 这样任何一个策略实现类都可以替换策略接口;
4. 接口隔离原则
Interface Segregation Principle
- 给应用上下文(调用方)提供尽可能小的接口; 避免暴露大接口;
- 多个专门的接口 > (优于) 一个功能复杂的总接口
接口隔离原则 跟 单一职责原则 有相似的地方, 都是讲求实现逻辑的精简, 避免暴露过多非必要的功能给调用方.
一来是为了设计简单, 可读性强;
二来是为了避免提供的功能过多, 导致后期的变更导致尾大不掉, 增加维护性的成本, 给扩展也带来更大压力!
[ˌseɡrɪˈɡeɪʃn] n.隔离;分离;种族隔离
5. 依赖倒置原则
Dependency Inversion Principle
应用程序, 应该面向接口编程, 不要针对实现编程;
依赖倒转的倒转 指的是: 我们原来一个对象依赖另外一个对象, 直接在 new 目标类, 干就完了;
但是现在我们要把这个 new 目标类 换成一个目标类的接口, 我们使用接口的方法;
把依赖的具体倒置为依赖接口(抽象)!
设计模式案例:
模板方法模式
我们应用程序中使用模板类, 模板类中留好未实现的方法逻辑, 在程序实际执行过程中我们调用的这块的逻辑实际上来自于下层的抽象类的实现; 这样, 依赖的关系就倒转了.
工厂方法模式
工厂方法模式也是: 我们依赖于工厂的抽象来调用, 调用时实际会唤起具体的工厂实现子类.
6. 合成/聚合复用原则
什么是合成和聚合?
类和类之间有3中关系: 一般化关系/关联关系/依赖关系:
6.1 一般化关系
- 类和类之间的继承关系
- 接口和接口之间的继承关系
- 类和接口之间的实现关系
6.2 关联关系
关联关系 就是一个类知道另一个类的属性和方法, 简单说, 就是实例变量的引用;
关联关系
一个类引用了另一个类的实例变量, 作为自己的属性;
聚合关系
强的关联关系; 一个类作为整体, 关联了一些个体类的实例;
合成关系
比聚合更强的关联关系: 一个类作为整体, 关联了一些其他类, 被关联的类和他是整体和部分的关系, 整个生命周期都在其控制之内;
具体说明:
关联 : 体现在java代码中就是: 一个类中引用了另一个类的示例(引用示例), 比如: spring的Service类中@Autowire一个别得服务;
聚合: 聚合也是一种关联; 只是加强了这种关联; 聚合是一种强关联: 整体和个体的那种关联, 典型例子就是: 汽车和轮胎/汽车和发动机;
汽车是整体, 发动机和轮胎都是个体;
合成: 合成也是一种关联; 且是更强的关联, 合成也是一种强关联, 比聚合更强, 合成的关联, 是整体和部分的关系, 整体整个负责了部分的生命周期; 典型例子: 人和胳膊/大腿/脚丫子; 人事整体, 胳膊/大腿/脚丫子都是部分, 且他们的生命周期都更强的关联于人体;
依赖: 依赖也是一种
6.3 CARP 合成聚合复用原则
是说如果要提高可复用性, 应该着眼于合成和聚合的这种关联关系, 而不是通过继承类来实现!
这里有jdk中的两个反例: 他们通过继承来实现复用, 不符合设计原则:
Stack类:
Stack<E> extends Vector<E>
Properties类:
Properties extends Hashtable<Object,Object>
7. 迪米特法则(最少通信)
迪米特法则, 也称最少知道原则: 软件实体之间应该尽可能少的发生相互作用;
最简单的优化逻辑就是: 降低成员变量的访问权限; 比如非必要, 就public->private
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 hi@niewj.com