auto commit
This commit is contained in:
228
notes/设计模式.md
228
notes/设计模式.md
@ -44,11 +44,11 @@
|
||||
|
||||
## 1. 单例(Singleton)
|
||||
|
||||
### 意图
|
||||
### Intent
|
||||
|
||||
确保一个类只有一个实例,并提供该实例的全局访问点。
|
||||
|
||||
### 类图
|
||||
### Class Diagram
|
||||
|
||||
使用一个私有构造函数、一个私有静态变量以及一个公有静态函数来实现。
|
||||
|
||||
@ -56,13 +56,13 @@
|
||||
|
||||
<div align="center"> <img src="../pics//562f2844-d77c-40e0-887a-28a7128abd42.png"/> </div><br>
|
||||
|
||||
### 实现
|
||||
### Implementation
|
||||
|
||||
(一)懒汉式-线程不安全
|
||||
#### Ⅰ 懒汉式-线程不安全
|
||||
|
||||
以下实现中,私有静态变量 uniqueInstance 被延迟实例化,这样做的好处是,如果没有用到该类,那么就不会实例化 uniqueInstance,从而节约资源。
|
||||
|
||||
这个实现在多线程环境下是不安全的,如果多个线程能够同时进入 `if (uniqueInstance == null)` ,并且此时 uniqueInstance 为 null,那么会有多个线程执行 `uniqueInstance = new Singleton();` 语句,这将导致多次实例化 uniqueInstance。
|
||||
这个实现在多线程环境下是不安全的,如果多个线程能够同时进入 `if (uniqueInstance == null)` ,并且此时 uniqueInstance 为 null,那么会有多个线程执行 `uniqueInstance = new Singleton();` 语句,这将导致实例化多次 uniqueInstance。
|
||||
|
||||
```java
|
||||
public class Singleton {
|
||||
@ -81,9 +81,9 @@ public class Singleton {
|
||||
}
|
||||
```
|
||||
|
||||
(二)饿汉式-线程安全
|
||||
#### Ⅱ 饿汉式-线程安全
|
||||
|
||||
线程不安全问题主要是由于 uniqueInstance 被多次实例化,采取直接实例化 uniqueInstance 的方式就不会产生线程不安全问题。
|
||||
线程不安全问题主要是由于 uniqueInstance 被实例化多次,采取直接实例化 uniqueInstance 的方式就不会产生线程不安全问题。
|
||||
|
||||
但是直接实例化的方式也丢失了延迟实例化带来的节约资源的好处。
|
||||
|
||||
@ -91,11 +91,11 @@ public class Singleton {
|
||||
private static Singleton uniqueInstance = new Singleton();
|
||||
```
|
||||
|
||||
(三)懒汉式-线程安全
|
||||
#### Ⅲ 懒汉式-线程安全
|
||||
|
||||
只需要对 getUniqueInstance() 方法加锁,那么在一个时间点只能有一个线程能够进入该方法,从而避免了多次实例化 uniqueInstance 的问题。
|
||||
只需要对 getUniqueInstance() 方法加锁,那么在一个时间点只能有一个线程能够进入该方法,从而避免了实例化多次 uniqueInstance。
|
||||
|
||||
但是当一个线程进入该方法之后,其它试图进入该方法的线程都必须等待,因此性能上有一定的损耗。
|
||||
但是当一个线程进入该方法之后,其它试图进入该方法的线程都必须等待,即使 uniqueInstance 已经被实例化了。这会让线程阻塞时间过程,因此该方法有性能问题,不推荐使用。
|
||||
|
||||
```java
|
||||
public static synchronized Singleton getUniqueInstance() {
|
||||
@ -106,7 +106,7 @@ public static synchronized Singleton getUniqueInstance() {
|
||||
}
|
||||
```
|
||||
|
||||
(四)双重校验锁-线程安全
|
||||
#### Ⅳ 双重校验锁-线程安全
|
||||
|
||||
uniqueInstance 只需要被实例化一次,之后就可以直接使用了。加锁操作只需要对实例化那部分的代码进行,只有当 uniqueInstance 没有被实例化时,才需要进行加锁。
|
||||
|
||||
@ -133,7 +133,7 @@ public class Singleton {
|
||||
}
|
||||
```
|
||||
|
||||
考虑下面的实现,也就是只使用了一个 if 语句。在 uniqueInstance == null 的情况下,如果两个线程同时执行 if 语句,那么两个线程就会同时进入 if 语句块内。虽然在 if 语句块内有加锁操作,但是两个线程都会执行 `uniqueInstance = new Singleton();` 这条语句,只是先后的问题,那么就会进行两次实例化,从而产生了两个实例。因此必须使用双重校验锁,也就是需要使用两个 if 语句。
|
||||
考虑下面的实现,也就是只使用了一个 if 语句。在 uniqueInstance == null 的情况下,如果两个线程都执行了 if 语句,那么两个线程都会进入 if 语句块内。虽然在 if 语句块内有加锁操作,但是两个线程都会执行 `uniqueInstance = new Singleton();` 这条语句,只是先后的问题,那么就会进行两次实例化。因此必须使用双重校验锁,也就是需要使用两个 if 语句。
|
||||
|
||||
```java
|
||||
if (uniqueInstance == null) {
|
||||
@ -145,19 +145,19 @@ if (uniqueInstance == null) {
|
||||
|
||||
uniqueInstance 采用 volatile 关键字修饰也是很有必要的。`uniqueInstance = new Singleton();` 这段代码其实是分为三步执行。
|
||||
|
||||
1. 分配内存空间
|
||||
2. 初始化对象
|
||||
1. 为 uniqueInstance 分配内存空间
|
||||
2. 初始化 uniqueInstance
|
||||
3. 将 uniqueInstance 指向分配的内存地址
|
||||
|
||||
但是由于 JVM 具有指令重排的特性,有可能执行顺序变为了 1>3>2,这在单线程情况下自然是没有问题。但如果是多线程下,有可能获得是一个还没有被初始化的实例,以致于程序出错。
|
||||
但是由于 JVM 具有指令重排的特性,执行顺序有可能变成 1>3>2。指令重排在单线程环境下不会出先问题,但是在多线程环境下会导致一个线程获得还没有初始化的实例。例如,线程 T<sub>1</sub> 执行了 1 和 3,此时 T<sub>2</sub> 调用 getUniqueInstance() 后发现 uniqueInstance 不为空,因此返回 uniqueInstance,但此时 uniqueInstance 还未被初始化。
|
||||
|
||||
使用 volatile 可以禁止 JVM 的指令重排,保证在多线程环境下也能正常运行。
|
||||
|
||||
(五)静态内部类实现
|
||||
#### Ⅴ 静态内部类实现
|
||||
|
||||
当 Singleton 类加载时,静态内部类 SingletonHolder 没有被加载进内存。只有当调用 `getUniqueInstance()` 方法从而触发 `SingletonHolder.INSTANCE` 时 SingletonHolder 才会被加载,此时初始化 INSTANCE 实例。
|
||||
当 Singleton 类加载时,静态内部类 SingletonHolder 没有被加载进内存。只有当调用 `getUniqueInstance()` 方法从而触发 `SingletonHolder.INSTANCE` 时 SingletonHolder 才会被加载,此时初始化 INSTANCE 实例,并且 JVM 能确保 INSTANCE 只被实例化一次。
|
||||
|
||||
这种方式不仅具有延迟初始化的好处,而且由虚拟机提供了对线程安全的支持。
|
||||
这种方式不仅具有延迟初始化的好处,而且由 JVM 提供了对线程安全的支持。
|
||||
|
||||
```java
|
||||
public class Singleton {
|
||||
@ -175,40 +175,11 @@ public class Singleton {
|
||||
}
|
||||
```
|
||||
|
||||
(六)枚举实现
|
||||
该实现在多次序列化再进行反序列化之后,不会得到多个实例。而其它实现,为了保证不会出现反序列化之后出现多个实例,需要使用 transient 修饰所有字段,并且实现序列化和反序列化的方法。
|
||||
|
||||
这是单例模式的最佳实践,它实现简单,并且在面对复杂的序列化或者反射攻击的时候,能够防止实例化多次。
|
||||
该实现可以防止反射攻击。在其它实现中,通过 setAccessible() 方法可以将私有构造函数的访问级别设置为 public,然后调用构造函数从而实例化对象,如果要防止这种攻击,需要在构造函数中添加防止实例化第二个对象的代码。但是该实现是由 JVM 保证只会实例化一次,因此不会出现上述的反射攻击。
|
||||
|
||||
```java
|
||||
public enum Singleton {
|
||||
uniqueInstance;
|
||||
}
|
||||
```
|
||||
|
||||
考虑以下单例模式的实现,该 Singleton 在每次序列化的时候都会创建一个新的实例,为了保证只创建一个实例,必须声明所有字段都是 transient,并且提供一个 readResolve() 方法。
|
||||
|
||||
```java
|
||||
public class Singleton implements Serializable {
|
||||
|
||||
private static Singleton uniqueInstance;
|
||||
|
||||
private Singleton() {
|
||||
}
|
||||
|
||||
public static synchronized Singleton getUniqueInstance() {
|
||||
if (uniqueInstance == null) {
|
||||
uniqueInstance = new Singleton();
|
||||
}
|
||||
return uniqueInstance;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
如果不使用枚举来实现单例模式,会出现反射攻击,因为通过 setAccessible() 方法可以将私有构造函数的访问级别设置为 public,然后调用构造函数从而实例化对象。如果要防止这种攻击,需要在构造函数中添加防止实例化第二个对象的代码。
|
||||
|
||||
从上面的讨论可以看出,解决序列化和反射攻击很麻烦,而枚举实现不会出现这两种问题,所以说枚举实现单例模式是最佳实践。
|
||||
|
||||
### 使用场景
|
||||
### Examples
|
||||
|
||||
- Logger Classes
|
||||
- Configuration Classes
|
||||
@ -223,11 +194,11 @@ public class Singleton implements Serializable {
|
||||
|
||||
## 2. 简单工厂(Simple Factory)
|
||||
|
||||
### 意图
|
||||
### Intent
|
||||
|
||||
在创建一个对象时不向客户暴露内部细节,并提供一个创建对象的通用接口。
|
||||
|
||||
### 类图
|
||||
### Class Diagram
|
||||
|
||||
简单工厂不是设计模式,更像是一种编程习惯。它把实例化的操作单独放到一个类中,这个类就成为简单工厂类,让简单工厂类来决定应该用哪个具体子类来实例化。
|
||||
|
||||
@ -235,7 +206,7 @@ public class Singleton implements Serializable {
|
||||
|
||||
<div align="center"> <img src="../pics//c79da808-0f28-4a36-bc04-33ccc5b83c13.png"/> </div><br>
|
||||
|
||||
### 实现
|
||||
### Implementation
|
||||
|
||||
```java
|
||||
public interface Product {
|
||||
@ -303,11 +274,11 @@ public class Client {
|
||||
|
||||
## 3. 工厂方法(Factory Method)
|
||||
|
||||
### 意图
|
||||
### Intent
|
||||
|
||||
定义了一个创建对象的接口,但由子类决定要实例化哪个类。工厂方法把实例化操作推迟到子类。
|
||||
|
||||
### 类图
|
||||
### Class Diagram
|
||||
|
||||
在简单工厂中,创建对象的是另一个类,而在工厂方法中,是由子类来创建对象。
|
||||
|
||||
@ -315,7 +286,7 @@ public class Client {
|
||||
|
||||
<div align="center"> <img src="../pics//1818e141-8700-4026-99f7-900a545875f5.png"/> </div><br>
|
||||
|
||||
### 实现
|
||||
### Implementation
|
||||
|
||||
```java
|
||||
public abstract class Factory {
|
||||
@ -363,11 +334,11 @@ public class ConcreteFactory2 extends Factory {
|
||||
|
||||
## 4. 抽象工厂(Abstract Factory)
|
||||
|
||||
### 意图
|
||||
### Intent
|
||||
|
||||
提供一个接口,用于创建 **相关的对象家族** 。
|
||||
|
||||
### 类图
|
||||
### Class Diagram
|
||||
|
||||
抽象工厂模式创建的是对象家族,也就是很多对象而不是一个对象,并且这些对象是相关的,也就是说必须一起创建出来。而工厂方法模式只是用于创建一个对象,这和抽象工厂模式有很大不同。
|
||||
|
||||
@ -379,7 +350,7 @@ public class ConcreteFactory2 extends Factory {
|
||||
|
||||
<div align="center"> <img src="../pics//8668a3e1-c9c7-4fcb-98b2-a96a5d841579.png"/> </div><br>
|
||||
|
||||
### 代码实现
|
||||
### Implementation
|
||||
|
||||
```java
|
||||
public class AbstractProductA {
|
||||
@ -461,15 +432,15 @@ public class Client {
|
||||
|
||||
## 5. 生成器(Builder)
|
||||
|
||||
### 意图
|
||||
### Intent
|
||||
|
||||
封装一个对象的构造过程,并允许按步骤构造。
|
||||
|
||||
### 类图
|
||||
### Class Diagram
|
||||
|
||||
<div align="center"> <img src="../pics//13b0940e-d1d7-4b17-af4f-b70cb0a75e08.png"/> </div><br>
|
||||
|
||||
### 实现
|
||||
### Implementation
|
||||
|
||||
以下是一个简易的 StringBuilder 实现,参考了 JDK 1.8 源码。
|
||||
|
||||
@ -551,15 +522,15 @@ abcdefghijklmnopqrstuvwxyz
|
||||
|
||||
## 6. 原型模式(Prototype)
|
||||
|
||||
### 意图
|
||||
### Intent
|
||||
|
||||
使用原型实例指定要创建对象的类型,通过复制这个原型来创建新对象。
|
||||
|
||||
### 类图
|
||||
### Class Diagram
|
||||
|
||||
<div align="center"> <img src="../pics//a40661e4-1a71-46d2-a158-ff36f7fc3331.png"/> </div><br>
|
||||
|
||||
### 实现
|
||||
### Implementation
|
||||
|
||||
```java
|
||||
public abstract class Prototype {
|
||||
@ -610,17 +581,17 @@ abc
|
||||
|
||||
## 1. 责任链(Chain Of Responsibility)
|
||||
|
||||
### 意图
|
||||
### Intent
|
||||
|
||||
使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链发送该请求,直到有一个对象处理它为止。
|
||||
|
||||
### 类图
|
||||
### Class Diagram
|
||||
|
||||
- Handler:定义处理请求的接口,并且实现后继链(successor)
|
||||
|
||||
<div align="center"> <img src="../pics//691f11eb-31a7-46be-9de1-61f433c4b3c7.png"/> </div><br>
|
||||
|
||||
### 实现
|
||||
### Implementation
|
||||
|
||||
```java
|
||||
public abstract class Handler {
|
||||
@ -724,11 +695,16 @@ request2 is handle by ConcreteHandler2
|
||||
|
||||
## 2. 命令(Command)
|
||||
|
||||
### 意图
|
||||
### Intent
|
||||
|
||||
将命令封装成对象中,以便使用命令来参数化其它对象,或者将命令对象放入队列中进行排队,或者将命令对象的操作记录到日志中,以及支持可撤销的操作。
|
||||
将命令封装成对象中,具有以下作用:
|
||||
|
||||
### 类图
|
||||
- 使用命令来参数化其它对象
|
||||
- 将命令放入队列中进行排队
|
||||
- 将命令的操作记录到日志中
|
||||
- 支持可撤销的操作
|
||||
|
||||
### Class Diagram
|
||||
|
||||
- Command:命令
|
||||
- Receiver:命令接收者,也就是命令真正的执行者
|
||||
@ -737,7 +713,7 @@ request2 is handle by ConcreteHandler2
|
||||
|
||||
<div align="center"> <img src="../pics//ae1b27b8-bc13-42e7-ac12-a2242e125499.png"/> </div><br>
|
||||
|
||||
### 实现
|
||||
### Implementation
|
||||
|
||||
设计一个遥控器,可以控制电灯开关。
|
||||
|
||||
@ -847,18 +823,18 @@ public class Client {
|
||||
|
||||
## 3. 解释器(Interpreter)
|
||||
|
||||
### 意图
|
||||
### Intent
|
||||
|
||||
为语言创建解释器,通常由语言的语法和语法分析来定义。
|
||||
|
||||
### 类图
|
||||
### Class Diagram
|
||||
|
||||
- TerminalExpression:终结符表达式,每个终结符都需要一个 TerminalExpression
|
||||
- Context:上下文,包含解释器之外的一些全局信息
|
||||
- TerminalExpression:终结符表达式,每个终结符都需要一个 TerminalExpression。
|
||||
- Context:上下文,包含解释器之外的一些全局信息。
|
||||
|
||||
<div align="center"> <img src="../pics//794239e3-4baf-4aad-92df-f02f59b2a6fe.png"/> </div><br>
|
||||
|
||||
### 实现
|
||||
### Implementation
|
||||
|
||||
以下是一个规则检验器实现,具有 and 和 or 规则,通过规则可以构建一颗解析树,用来检验一个文本是否满足解析树定义的规则。
|
||||
|
||||
@ -971,11 +947,11 @@ false
|
||||
|
||||
## 4. 迭代器(Iterator)
|
||||
|
||||
### 意图
|
||||
### Intent
|
||||
|
||||
提供一种顺序访问聚合对象元素的方法,并且不暴露聚合对象的内部表示。
|
||||
|
||||
### 类图
|
||||
### Class Diagram
|
||||
|
||||
- Aggregate 是聚合类,其中 createIterator() 方法可以产生一个 Iterator;
|
||||
- Iterator 主要定义了 hasNext() 和 next() 方法。
|
||||
@ -983,7 +959,7 @@ false
|
||||
|
||||
<div align="center"> <img src="../pics//b0f61ac2-a4b6-4042-9cf0-ccf4238c1ff7.png"/> </div><br>
|
||||
|
||||
### 实现
|
||||
### Implementation
|
||||
|
||||
```java
|
||||
public interface Aggregate {
|
||||
@ -1059,18 +1035,18 @@ public class Client {
|
||||
|
||||
## 5. 中介者(Mediator)
|
||||
|
||||
### 意图
|
||||
### Intent
|
||||
|
||||
集中相关对象之间复杂的沟通和控制方式。
|
||||
|
||||
### 类图
|
||||
### Class Diagram
|
||||
|
||||
- Mediator:中介者,定义一个接口用于与各同事(Colleague)对象通信。
|
||||
- Colleague:同事,相关对象
|
||||
|
||||
<div align="center"> <img src="../pics//d0afdd23-c9a5-4d1c-9b3d-404bff3bd0d1.png"/> </div><br>
|
||||
|
||||
### 实现
|
||||
### Implementation
|
||||
|
||||
Alarm(闹钟)、CoffeePot(咖啡壶)、Calendar(日历)、Sprinkler(喷头)是一组相关的对象,在某个对象的事件产生时需要去操作其它对象,形成了下面这种依赖结构:
|
||||
|
||||
@ -1228,11 +1204,11 @@ doSprinkler()
|
||||
|
||||
## 6. 备忘录(Memento)
|
||||
|
||||
### 意图
|
||||
### Intent
|
||||
|
||||
在不违反封装的情况下获得对象的内部状态,从而在需要时可以将对象恢复到最初状态。
|
||||
|
||||
### 类图
|
||||
### Class Diagram
|
||||
|
||||
- Originator:原始对象
|
||||
- Caretaker:负责保存好备忘录
|
||||
@ -1240,7 +1216,7 @@ doSprinkler()
|
||||
|
||||
<div align="center"> <img src="../pics//867e93eb-3161-4f39-b2d2-c0cd3788e194.png"/> </div><br>
|
||||
|
||||
### 实现
|
||||
### Implementation
|
||||
|
||||
以下实现了一个简单计算器程序,可以输入两个值,然后计算这两个值的和。备忘录模式允许将这两个值存储起来,然后在某个时刻用存储的状态进行恢复。
|
||||
|
||||
@ -1405,7 +1381,7 @@ public class Client {
|
||||
|
||||
## 7. 观察者(Observer)
|
||||
|
||||
### 意图
|
||||
### Intent
|
||||
|
||||
定义对象之间的一对多依赖,当一个对象状态改变时,它的所有依赖都会收到通知并且自动更新状态。
|
||||
|
||||
@ -1413,7 +1389,7 @@ public class Client {
|
||||
|
||||
<div align="center"> <img src="../pics//7a3c6a30-c735-4edb-8115-337288a4f0f2.jpg" width="600"/> </div><br>
|
||||
|
||||
### 类图
|
||||
### Class Diagram
|
||||
|
||||
主题(Subject)具有注册和移除观察者、并通知所有观察者的功能,主题是通过维护一张观察者列表来实现这些操作的。
|
||||
|
||||
@ -1421,7 +1397,7 @@ public class Client {
|
||||
|
||||
<div align="center"> <img src="../pics//0df5d84c-e7ca-4e3a-a688-bb8e68894467.png"/> </div><br>
|
||||
|
||||
### 实现
|
||||
### Implementation
|
||||
|
||||
天气数据布告板会在天气信息发生改变时更新其内容,布告板有多个,并且在将来会继续增加。
|
||||
|
||||
@ -1540,15 +1516,15 @@ StatisticsDisplay.update: 1.0 1.0 1.0
|
||||
|
||||
## 8. 状态(State)
|
||||
|
||||
### 意图
|
||||
### Intent
|
||||
|
||||
允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它所属的类。
|
||||
|
||||
### 类图
|
||||
### Class Diagram
|
||||
|
||||
<div align="center"> <img src="../pics//c5085437-54df-4304-b62d-44b961711ba7.png"/> </div><br>
|
||||
|
||||
### 实现
|
||||
### Implementation
|
||||
|
||||
糖果销售机有多种状态,每种状态下销售机有不同的行为,状态可以发生转移,使得销售机的行为也发生改变。
|
||||
|
||||
@ -1842,16 +1818,16 @@ No gumball dispensed
|
||||
|
||||
## 9. 策略(Strategy)
|
||||
|
||||
### 意图
|
||||
### Intent
|
||||
|
||||
定义一系列算法,封装每个算法,并使它们可以互换。
|
||||
|
||||
策略模式可以让算法独立于使用它的客户端。
|
||||
|
||||
### 类图
|
||||
### Class Diagram
|
||||
|
||||
- Strategy 接口定义了一个算法族,它们都具有 behavior() 方法。
|
||||
- Context 是使用到该算法族的类,其中的 doSomething() 方法会调用 behavior(),setStrategy(in Strategy) 方法可以动态地改变 strategy 对象,也就是说能动态地改变 Context 所使用的算法。
|
||||
- Context 是使用到该算法族的类,其中的 doSomething() 方法会调用 behavior(),setStrategy(Strategy) 方法可以动态地改变 strategy 对象,也就是说能动态地改变 Context 所使用的算法。
|
||||
|
||||
<div align="center"> <img src="../pics//1fc969e4-0e7c-441b-b53c-01950d2f2be5.png"/> </div><br>
|
||||
|
||||
@ -1861,7 +1837,7 @@ No gumball dispensed
|
||||
|
||||
状态模式主要是用来解决状态转移的问题,当状态发生转移了,那么 Context 对象就会改变它的行为;而策略模式主要是用来封装一组可以互相替代的算法族,并且可以根据需要动态地去替换 Context 使用的算法。
|
||||
|
||||
### 实现
|
||||
### Implementation
|
||||
|
||||
设计一个鸭子,它可以动态地改变叫声。这里的算法族是鸭子的叫声行为。
|
||||
|
||||
@ -1930,17 +1906,17 @@ quack!
|
||||
|
||||
## 10. 模板方法(Template Method)
|
||||
|
||||
### 意图
|
||||
### Intent
|
||||
|
||||
定义算法框架,并将一些步骤的实现延迟到子类。
|
||||
|
||||
通过模板方法,子类可以重新定义算法的某些步骤,而不用改变算法的结构。
|
||||
|
||||
### 类图
|
||||
### Class Diagram
|
||||
|
||||
<div align="center"> <img src="../pics//c3c1c0e8-3a78-4426-961f-b46dd0879dd8.png"/> </div><br>
|
||||
|
||||
### 实现
|
||||
### Implementation
|
||||
|
||||
冲咖啡和冲茶都有类似的流程,但是某些步骤会有点不一样,要求复用那些相同步骤的代码。
|
||||
|
||||
@ -2031,11 +2007,11 @@ Tea.addCondiments
|
||||
|
||||
## 11. 访问者(Visitor)
|
||||
|
||||
### 意图
|
||||
### Intent
|
||||
|
||||
为一个对象结构(比如组合结构)增加新能力。
|
||||
|
||||
### 类图
|
||||
### Class Diagram
|
||||
|
||||
- Visitor:访问者,为每一个 ConcreteElement 声明一个 visit 操作
|
||||
- ConcreteVisitor:具体访问者,存储遍历过程中的累计结果
|
||||
@ -2043,7 +2019,7 @@ Tea.addCondiments
|
||||
|
||||
<div align="center"> <img src="../pics//ec923dc7-864c-47b0-a411-1f2c48d084de.png"/> </div><br>
|
||||
|
||||
### 实现
|
||||
### Implementation
|
||||
|
||||
```java
|
||||
public interface Element {
|
||||
@ -2238,17 +2214,17 @@ Number of items: 6
|
||||
|
||||
## 12. 空对象(Null)
|
||||
|
||||
### 意图
|
||||
### Intent
|
||||
|
||||
使用什么都不做的空对象来代替 NULL。
|
||||
|
||||
一个方法返回 NULL,意味着方法的调用端需要去检查返回值是否是 NULL,这么做会导致非常多的冗余的检查代码。并且如果某一个调用端忘记了做这个检查返回值,而直接使用返回的对象,那么就有可能抛出空指针异常。
|
||||
|
||||
### 类图
|
||||
### Class Diagram
|
||||
|
||||
<div align="center"> <img src="../pics//dd3b289c-d90e-44a6-a44c-4880517eb1de.png"/> </div><br>
|
||||
|
||||
### 实现
|
||||
### Implementation
|
||||
|
||||
```java
|
||||
public abstract class AbstractOperation {
|
||||
@ -2294,17 +2270,17 @@ public class Client {
|
||||
|
||||
## 1. 适配器(Adapter)
|
||||
|
||||
### 意图
|
||||
### Intent
|
||||
|
||||
把一个类接口转换成另一个用户需要的接口。
|
||||
|
||||
<div align="center"> <img src="../pics//3d5b828e-5c4d-48d8-a440-281e4a8e1c92.png"/> </div><br>
|
||||
|
||||
### 类图
|
||||
### Class Diagram
|
||||
|
||||
<div align="center"> <img src="../pics//0f754c1d-b5cb-48cd-90e0-4a86034290a1.png"/> </div><br>
|
||||
|
||||
### 实现
|
||||
### Implementation
|
||||
|
||||
鸭子(Duck)和火鸡(Turkey)拥有不同的叫声,Duck 的叫声调用 quack() 方法,而 Turkey 调用 gobble() 方法。
|
||||
|
||||
@ -2365,18 +2341,18 @@ public class Client {
|
||||
|
||||
## 2. 桥接(Bridge)
|
||||
|
||||
### 意图
|
||||
### Intent
|
||||
|
||||
将抽象与实现分离开来,使它们可以独立变化。
|
||||
|
||||
### 类图
|
||||
### Class Diagram
|
||||
|
||||
- Abstraction:定义抽象类的接口
|
||||
- Implementor:定义实现类接口
|
||||
|
||||
<div align="center"> <img src="../pics//c2cbf5d2-82af-4c78-bd43-495da5adf55f.png"/> </div><br>
|
||||
|
||||
### 实现
|
||||
### Implementation
|
||||
|
||||
RemoteControl 表示遥控器,指代 Abstraction。
|
||||
|
||||
@ -2518,11 +2494,11 @@ public class Client {
|
||||
|
||||
## 3. 组合(Composite)
|
||||
|
||||
### 意图
|
||||
### Intent
|
||||
|
||||
将对象组合成树形结构来表示“整体/部分”层次关系,允许用户以相同的方式处理单独对象和组合对象。
|
||||
|
||||
### 类图
|
||||
### Class Diagram
|
||||
|
||||
组件(Component)类是组合类(Composite)和叶子类(Leaf)的父类,可以把组合类看成是树的中间节点。
|
||||
|
||||
@ -2530,7 +2506,7 @@ public class Client {
|
||||
|
||||
<div align="center"> <img src="../pics//3fb5b255-b791-45b6-8754-325c8741855a.png"/> </div><br>
|
||||
|
||||
### 实现
|
||||
### Implementation
|
||||
|
||||
```java
|
||||
public abstract class Component {
|
||||
@ -2652,17 +2628,17 @@ Composite:root
|
||||
|
||||
## 4. 装饰(Decorator)
|
||||
|
||||
### 意图
|
||||
### Intent
|
||||
|
||||
为对象动态添加功能。
|
||||
|
||||
### 类图
|
||||
### Class Diagram
|
||||
|
||||
装饰者(Decorator)和具体组件(ConcreteComponent)都继承自组件(Component),具体组件的方法实现不需要依赖于其它对象,而装饰者组合了一个组件,这样它可以装饰其它装饰者或者具体组件。所谓装饰,就是把这个装饰者套在被装饰者之上,从而动态扩展被装饰者的功能。装饰者的方法有一部分是自己的,这属于它的功能,然后调用被装饰者的方法实现,从而也保留了被装饰者的功能。可以看到,具体组件应当是装饰层次的最低层,因为只有具体组件的方法实现不需要依赖于其它对象。
|
||||
|
||||
<div align="center"> <img src="../pics//137c593d-0a9e-47b8-a9e6-b71f540b82dd.png"/> </div><br>
|
||||
|
||||
### 实现
|
||||
### Implementation
|
||||
|
||||
设计不同种类的饮料,饮料可以添加配料,比如可以添加牛奶,并且支持动态添加新配料。每增加一种配料,该饮料的价格就会增加,要求计算一种饮料的价格。
|
||||
|
||||
@ -2759,15 +2735,15 @@ public class Client {
|
||||
|
||||
## 5. 外观(Facade)
|
||||
|
||||
### 意图
|
||||
### Intent
|
||||
|
||||
提供了一个统一的接口,用来访问子系统中的一群接口,从而让子系统更容易使用。
|
||||
|
||||
### 类图
|
||||
### Class Diagram
|
||||
|
||||
<div align="center"> <img src="../pics//f9978fa6-9f49-4a0f-8540-02d269ac448f.png"/> </div><br>
|
||||
|
||||
### 实现
|
||||
### Implementation
|
||||
|
||||
观看电影需要操作很多电器,使用外观模式实现一键看电影功能。
|
||||
|
||||
@ -2814,11 +2790,11 @@ public class Client {
|
||||
|
||||
## 6. 享元(Flyweight)
|
||||
|
||||
### 意图
|
||||
### Intent
|
||||
|
||||
利用共享的方式来支持大量细粒度的对象,这些对象一部分内部状态是相同的。
|
||||
|
||||
### 类图
|
||||
### Class Diagram
|
||||
|
||||
- Flyweight:享元对象
|
||||
- IntrinsicState:内部状态,享元对象共享内部状态
|
||||
@ -2826,7 +2802,7 @@ public class Client {
|
||||
|
||||
<div align="center"> <img src="../pics//d52270b4-9097-4667-9f18-f405fc661c99.png"/> </div><br>
|
||||
|
||||
### 实现
|
||||
### Implementation
|
||||
|
||||
```java
|
||||
public interface Flyweight {
|
||||
@ -2899,11 +2875,11 @@ Java 利用缓存来加速大量小对象的访问时间。
|
||||
|
||||
## 7. 代理(Proxy)
|
||||
|
||||
### 意图
|
||||
### Intent
|
||||
|
||||
控制对其它对象的访问。
|
||||
|
||||
### 类图
|
||||
### Class Diagram
|
||||
|
||||
代理有以下四类:
|
||||
|
||||
@ -2914,7 +2890,7 @@ Java 利用缓存来加速大量小对象的访问时间。
|
||||
|
||||
<div align="center"> <img src="../pics//a6c20f60-5eba-427d-9413-352ada4b40fe.png"/> </div><br>
|
||||
|
||||
### 实现
|
||||
### Implementation
|
||||
|
||||
以下是一个虚拟代理的实现,模拟了图片延迟加载的情况下使用与图片大小相等的临时内容去替换原始图片,直到图片加载完成才将图片显示出来。
|
||||
|
||||
|
Reference in New Issue
Block a user