文章目录
- 1. UML
- 2. 面向对象的设计原则
- 1)单一职责原则(Single Responsibility Principle, SRP)
- 2)开闭原则(Open-Closed Principle, OCP)
- 3)里氏替换原则(Liskov Substitution Principle, LSP)
- 4)依赖倒置原则(Dependence Inversion Principle, DIP)
- 5)接口隔离原则(Interface Segregation Principle, ISP)
- 6)迪米特法则(Law of Demeter, LoD)
- 7)合成复用原则(Composition/Aggregate Reuse Principle, CARP)
- 设计的核心思想
- 3. 设计模式
1. UML
1.1 类图
1.1.1 属性
以上对应的代码:
public class Employee {
public String name;
protected int age = 25;
private String email;
}
1.1.2 方法
1.2 类之间的关系
1.2.1 关联(association)
用于表示一类对象与另一类对象之间有联系,通常将一个类的对象作为另一个类的成员变量。
1.2.1.1 单向关联
1.2.1.2 双向关联
1.2.1.3 自关联
1.2.1.4 多重性关联
表示两个关联对象在数量上的对应关系
比如,一个界面可以包含0个或多个按钮,但一个按钮只能属于一个界面
1.2.1.5 聚合(Aggregation)
- 聚合关系表示类之间整体与部分的关系
- 在聚合关系中,成员对象是整体对象的一部分,但是成员对象可以脱离整体对象独立存在
- 在UML中,聚合关系用带空心菱形的直线表示
注入方式:setter注入、构造器注入
1.2.1.6 组合(Composition)
- 组合关系表示类之间整体与部分的关系
- 在组合关系中,成员对象与整体对象之间具有同生共死的关系,一旦整体对象不存在,成员对象也将不存在
- 在UML中,组合关系用带实心菱形的直线表示
通常,在整体类的构造函数中就会初始化部分类的实例。
1.2.2 依赖(Dependency)
依赖关系的三种表现:
- 在一个类的方法中将另一个类的对象作为其方法参数
- 在一个类的方法中将另一个类的对象作为其局部变量
- 在一个类的方法中调用另一个类的静态方法
1.2.3 继承(Generalization)
1.2.4 实现(Realization)
1.3 注释
2. 面向对象的设计原则
1)单一职责原则(Single Responsibility Principle, SRP)
定义:一个对象应该只包含单一的职责,并且该职责被完整地封装在一个类中。
就一个类而言,应该仅有一个引起它变化的原因。
分析:
- 一个类承担的职责越多,当某个职责变化时,可能会影响其他职责的运作,这就会降低该类的可复用性
- 将职责进行分离,不同的职责封装在不同的类中
- 将不同的变化原因封装在不同的类中
- 单一职责原则是实现高内聚、低耦合的指导方针
2)开闭原则(Open-Closed Principle, OCP)
定义:对扩展开放(对提供方), 对修改关闭(对使用方)。
分析:
- 抽象化是开闭原则的关键
- 相对稳定的抽象层 + 灵活的具体层。用抽象构建框架,用实现扩展细节。
- 找到系统的可变因素并将其封装起来
开闭原则是最重要的原则,它是面向对象设计的目标。
3)里氏替换原则(Liskov Substitution Principle, LSP)
定义:所有引用父类的地方必须能透明地使用其子类的对象
分析:
- 在软件中将一个基类对象替换成它的子类对象,程序将不会产生任何错误和异常
- 在使用继承时,遵循里氏替换原则,在子类中尽量不要重写父类的方法
- 在程序中尽量使用基类类型来对对象进行定义,而在运行时再确定其子类类型
- 里氏替换原则指明了继承实际上增强了类之间的耦合,在适当的情况下,应考虑聚合、组合、依赖而不是继承
里氏替换是实现OCP的重要方式之一。
4)依赖倒置原则(Dependence Inversion Principle, DIP)
定义:高层模块不应该依赖低层模块,二者都应该依赖抽象。抽象不应该依赖细节,细节应该依赖抽象。
面向接口编程,而不是针对实现编程
分析:
- 传递参数时或在关联关系中,尽量引用层次高的抽象层类。即,使用接口和抽象类进行变量类型声明、参数类型声明、方法返回类型声明,以及数据类型的转换等
- 在程序中尽量使用抽象层进行编程,而将具体类写在配置文件中
- 针对抽象层编程,将具体类的对象通过依赖注入(Dependency Injection, DI)的方式注入到其他对象
DI 的三种方式
- 接口注入
- 构造方法注入
- setter方式注入
OCP是面向对象设计的目标,而DIP则是面向对象设计的实现。
大多数情况下,OCP、LSP 和 DIP 会同时出现。OCP是目标,LSP是基础,DIP是手段
5)接口隔离原则(Interface Segregation Principle, ISP)
定义:一个类对另一个类的依赖应该建立在最小的接口上。即客户端不应该依赖那些它不需要的接口。
分析:将大接口中的方法根据其职责的不同分别放在不同的小接口中,以确保每个接口使用起来都较为方便,并职责单一
6)迪米特法则(Law of Demeter, LoD)
定义:一个类对自己依赖的类知道的越少越好。类与类关系越密切,耦合度越大。
LoD可以降低类之间的耦合(注意是降低,而不是要求完全没有依赖关系
分析:
- 迪米特法则要求在设计系统时,应该尽量减少对象之间的交互
- 如果两个对象之间不必彼此直接通信,那么这两个对象就不应该发生任何直接的相互作用
- 如果其中一个对象需要调用另一个对象的方法,可以通过“第三者”转发这个调用
- 通过引入一个合理的“第三者”(中间类)来降低现有对象之间的耦合度
一个对象只与直接朋友发生交互,“不和陌生人说话”
- 直接朋友
- 当前对象本身(this)
- 以形参形式传入到当前对象方法中的对象
- 当前对象的成员对象
- 如果当前对象的成员对象是一个集合,那么集合中的元素也都是朋友
- 当前对象所创建的对象
7)合成复用原则(Composition/Aggregate Reuse Principle, CARP)
定义:优先使用对象组合/聚合,而不是继承,从而达到复用的目的。
继承破坏了封装性,将父类的内部细节暴露给了子类,是一种“白箱复用”
组合/聚合是一种"黑箱复用",局部对象的内部实现细节对整体对象是不可见的
分析:
- 合成复用原则就是在一个新的对象里通过关联关系来使用一些已有的对象,使之成为新对象的一部分
- 新对象通过委派调用已有对象的方法达到复用功能的目的
- 复用时要尽量使用组合/聚合关系(关联关系),少用继承
设计的核心思想
- 找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起(封装变化点)
- 针对接口编程,而不是针对实现编程(面向接口编程)
- 为了交互对象之间的松耦合设计而努力 (多使用组合/聚合,而不是继承)
3. 设计模式
3.1 设计模式的四大要素
- 模式名称
- 大多数模式是根据其功能或模式结构来命名的
- 问题描述
- 应该在何时使用模式,它包含了设计中存在的问题以及问题存在的原因
- 解决方案(类图)
- 每一个设计模式的类图,类图中每一个角色的意义以及它们之间的关系
- 实现该模式的一些核心代码
- 效果
- 模式的优缺点,满足了哪些设计原则
每个模式至少应该掌握如下几点:
- 这个模式的意图是什么
- 它要解决一个什么问题
- 什么时候可以使用它
- 它是如何解决问题的
- 掌握它的结构图
- 记住它的关键代码
- 能够想到至少两个它的应用实例。一个生活中的,一个软件中的
- 这个模式的优缺点是什么
- 使用时要注意什么
3.2 23种设计模式的分类
- 创建型(Creational):主要描述如何创建对象
- 工厂方法模式(Factory Method)
- 抽象工厂模式(Abstract Factory)
- 单例模式(Singleton)
- 建造者模式(Builder)
- 原型模式(Prototype)
- 结构型(Structural):主要描述如何实现类或对象的组合
- 适配器模式(Adapter)
- 桥接模式(Bridge)
- 组合模式(Composite)
- 装饰模式(Decorator)
- 外观模式(Facade)
- 享元模式(Flyweight)
- 代理模式(Proxy)
- 行为型(Behavioral):主要描述类或对象怎样交互和怎样分配职责
- 职责链模式(Chain of Responsibility)
- 命令模式(Command)
- 解释器模式(Interpreter)
- 迭代器模式(Iterator)
- 中介者模式(Mediator)
- 备忘录模式(Memento)
- 观察者模式(Observer)
- 状态模式(State)
- 策略模式(Strategy)
- 模板方法模式(Template Method)
- 访问者模式(Visitor)