设计模式:面向对象设计原则
设计模式:面向对象设计原则
作者:wlai
发布:2019-12-01
更新:2025-02-09
一、重新认识(抽象层面的)面向对象
变化是软件复用的天敌!面向对象设计最大的优势是抵御变化!
- (1)理解隔离变化
从宏观层面来看,面向对象的构建方式更能适应软件的变化,能将变化所带来的影响减为最小。
- (2)各司其职
从微观层面来看,面向对象的方式更强调各个类的责任。由于需求变化导致的新增类型不应该影响原来类型的实现——即所谓的各负其责。
- (3)对象是什么?
从语言实现层面来看,对象封装了代码和数据;从规格层面讲,对象是一系列可被使用的公共接口;从概念层面讲,对象是某种拥有责任的抽象
二、面向对象设计原则
2.1 依赖倒置原则(DIP)
(1)高层模块(稳定)不应该依赖于底层模块(变化),二者都应该依赖于抽象(稳定);
(2)抽象(稳定)不应该依赖于实现细节(变化),实现细节应该依赖于抽象(稳定);
通过Shape
这个抽象来隔离Line
和Rect
的变化!
2.2 开放封闭原则(OCP)
(1)对扩展开放,对更改封闭
(2)类模块应该是可扩展的,但是不可修改
2.3 单一职责原则(SRP)
(1)一个类应该仅有一个引起它变化的原因
(2)变化的方向隐含着类的责任
当一个类含有几十个成员变量或者方法时,往往说明你所设计的类可能承担了过多的职责。正常设计良好的类不会表现地如此臃肿。
2.4 Liskov替换原则(LSP)
(1)子类必须能够替换它们的基类(IS-A)
(2)继承表达类型抽象
当子类无法替换基类时,应该想一想子类和父类之间是否真的是继承关系?有没有可能它们之间是组合关系?
2.5 接口隔离原则(ISP)
(1)不应该强迫客户程序依赖它们不用的方法
(2)接口应该小而完备
接口应该小而完备意味着不要将不必要的方法暴露给用户程序。
注:C++访问控制修饰符
1 |
|
修饰符 | 类内 | 子类类内 | 类对象 | 子类对象 |
---|---|---|---|---|
public | ✓ | ✓ | ✓ | ✓ |
private | ✓ | × | × | × |
protected | ✓ | ✓ | × | × |
2.6 优先使用对象组合,而不是类继承
(1)类继承通常为“白箱复用”,对象组合通常为“黑箱复用”
(2)继承在某种程度上破坏了封装性,子类父类耦合度过高
(3)而对象组合则要求被组合的对象具有良好定义的外部接口,耦合度低
2.7 封装变化点
(1)使用封装来创建对象之间的分界层,让设计者可以在分界层的一侧进行修改,而不会对另一侧产生不良影响从而实现分界之间的松耦合
2.8 针对接口编程,而不是针对实现编程
(1)不将变量类型声明为某个特定的具体类,而是声明为某个接口
(2)客户程序无需获知对象的具体类型,只需要知道对象所具有的接口
(3)减少系统中各部分的依赖关系,从而实现“高内聚低耦合”的类型设计方案
三、设计模式的分类
从封装变化的角度对设计模式进行分类,有以下几种:
(1)组件协作
Template Method
Strategy
Observer / Event
(2)单一职责
Decorator
Bridge
(3)对象创建
Factory Method
Abstract Factory
Prototype
Builder
(4)对象性能
Singleton
Flyweight
(5)接口隔离
Facade
Proxy
Mediator
Adapter
(6)状态变化
Memento
State
(7)数据结构
Composite
Iterator
Chain of
Responsibility
(8)行为变化
Command
Visitor
(9)领域问题
- Interpreter
四、重构以获得模式
面向对象设计模式是“好的面向对象设计”,所谓“好的面向对象设计”指的是那些可以满足“应对变化,提高复用”的设计。
现代软件设计的特征是“需求的频繁变化”。设计模式的要点是“寻找变化的点,然后在变化点处应用设计模式,从而来更好地应对需求的变化”。“什么时候、什么地点应用设计模式”比“理解设计模式结构本身”更为重要。
设计模式的应用不宜先入为主,一上来就使用设计模式是对设计模式的最大误用。没有一步到位的设计模式。敏捷软件开发实践倡导的“Refactoring to Patterns”是目前普遍公认最好的使用设计模式的方法。
重构的关键技法
(1)静态绑定 –> 动态绑定
(2)早绑定 –> 晚绑定
(3)继承 –> 组合
(4)编译时依赖 –> 运行时依赖
(5)紧耦合 –> 松耦合