面向对象设计原则
目录
面向对象设计原则
1 SOLID 法则
S Single Responsibility Principle 单一贵任原则
O Open Closed Principle 开放封闭原则
L Liskov Substitution Principle 里氏替换原则
I Interface Segregation Principle 接口分离原则
D Dependency Inversion Principle 依赖倒置原则
1.1 单一职责原则
- 将过多的职责耦合在一个类中导致了脆弱设计
- 职责是变化的原因,只有一个变化的原因
- 如果应用程序变化的方式总是导致两个方法同时变化,则不应该分离他们
- 把业务规则和持久化子系统绑定在一起是自讨苦吃,这违反了单一职责原则。可以使用 Facade 或者 Proxy 模式进行重构,以解除这种耦合
- 软件设计要做的,就是发现职责并把那些职责相互分离
- 职责越单一,越利于复用
- 利于单元测试
- 高内聚,密切相关的放在一起;
1.2 开闭原则
- 对扩展开放,对修改闭合
- 面向对象可复用设计的第一块基石
- 其他设计原则是开一闭原则的的手段和工具
- 抽象是关键
- Linux 设备驱动:抽象,两数指针;封装,static
- 工厂方法模式符合,简单工厂模式部分符合(工厂类需要修改)
1.3 里氏替换原则
- 任何基类可以出现的地方,子类一定可以出现:
- ISA 关系是就行为而言的:
- 考星继承的合理性
- 反例:功能少于基类的派生类,正方形从长方形继承,派生类抛出基类不能预计的异常。
- 策略模式,客户程序依赖于策略的抽象父类,根据需要替换为具体的策略子类。
- 组合模式,客户程序以一种一致的方式(相同的接口)访问不同的子类(叶子节点和树枝节点)
1.4 接口隔离原则
- 使用多个专门的接口比使用单一的总接口要好
- 一个类对另外一个类的依赖性应当是建立在最小的接口上
- 为不同的客户提供不同的接口
- 修改影响到哪些客户,很明确
- JAVA 的接口,C++ 的多重继承
- 迭代模式,迭代器只为客户暴露有限的接口
1.5 依赖倒置原则
- 要依赖于抽象,不要依赖于具体
- 具体依赖抽象,而不是抽象依赖具体。
- 面向接口编程,而不是面向具体编程
- 使用接口和抽象类进行变量类型声明、参数类型声明、方法返还类型说明,以及数据类型的转换等。
- 一个对象持有另外一个具体对象的引用可能破坏了 DIP
- 抽象一一稳定的,单一的,不变的,共性,事物本质的特质,(核心业务逻辑,业务模型,领域模型)。
- 具体一一可变的,多样的,细节,具体实现。
2 更多法则
2.1 迪米特法则
- 只与你的朋友们通信(朋友圈,子系统,门面模式)
- 不要跟“陌生人”讲话。(最少依赖原则,老死不相往来)
- 通过朋友的朋友访问陌生人(中介者模式)
- 最少知识/最少依赖。
- 限定通信/依赖的宽度和深度 (a。getB(。getc。getD0)。fun(),依赖的深度太深,违反最少知识/最少依赖)
2.2 面向接口编程
- 面向抽象编程,抽象是稳定的,程序自身变的稳定,灵活度增加。
- 成员变量,函数参数,函数返回值,局部变量都是抽象类
- 函数参数:draw(Shape shape) Vs draw (Rectangle rectangle)
- 成员变量:Shape shape VS Rectangle rectangle
- 成本增加,多了抽象层
- 时机,确定需要灵活度是再增加抽象层
2.3 专家模式
- 谁拥有数据,谁就来履行相应的职责
- 反例:一个对象要频繁访问另一对象的数据,或调用另一对象的方法;纯数据类。
2.4 合成/聚合复用原则
-
优先使用合成/聚合复用,而不是继承
-
合成/聚合复用的优点
- 通过接口访问成分对象(抽象接口),耦合度低。
- 黑箱复用,因为成分对象的内部细节是容器对象所看不见的,符合最小知识/ 依赖。
- 可以在运行时间内动态进行(替换为新对象),10c 依赖注入
-
继承的缺点
- 继承复用破坏封装,因为继承将超类的实现细节暴露给子类。由于超类的内部细节常常是对于子类透明的,所以这种复用是透明的复用,又称“白箱”复用
- 如果超类发生改变,那么子类的实现也不得不发生改变。
- 从超类继承而来的实现是静态的,不可能在运行时间内发生改变
- 具体继承/抽象继承
-
松耦合-高内聚
-
高内聚,密切相关的放在一起;所有属性在在同一个计算中被用到, 有同样的生命周期。
- 反例,一组属性被一组成员函数使用,另一组属性被另一组成员函数使用;极端情况,某个属性仅被一个方法使用(应该成为局部变量,参数,或独立的对象)
-
耦合(或者依赖)关系的种类
- 具体耦合(Concrete Coupling)
- 抽象耦合(Abstract Coupling),使用接口和抽象类进行变量类型声明、参数类型声明、方法返还类型说明,以及数据类型的转换等。
-
对象之间的关系:依赖,关联,聚合,组合
-
单一职责在讲高内聚,迪米特法则在讲松耦合。