设计模式

This commit is contained in:
Estom
2021-04-15 22:57:08 +08:00
parent 9da7efc987
commit 9e23f6ffa7
24 changed files with 1770 additions and 1449 deletions

65
.vscode/settings.json vendored
View File

@@ -14,6 +14,69 @@
"xlocmon": "cpp",
"xlocnum": "cpp",
"xstring": "cpp",
"xtree": "cpp"
"xtree": "cpp",
"algorithm": "cpp",
"atomic": "cpp",
"bit": "cpp",
"cctype": "cpp",
"chrono": "cpp",
"clocale": "cpp",
"cmath": "cpp",
"compare": "cpp",
"concepts": "cpp",
"condition_variable": "cpp",
"cstddef": "cpp",
"cstdint": "cpp",
"cstdio": "cpp",
"cstdlib": "cpp",
"cstring": "cpp",
"ctime": "cpp",
"cwchar": "cpp",
"exception": "cpp",
"coroutine": "cpp",
"resumable": "cpp",
"forward_list": "cpp",
"fstream": "cpp",
"functional": "cpp",
"future": "cpp",
"initializer_list": "cpp",
"iomanip": "cpp",
"ios": "cpp",
"iosfwd": "cpp",
"istream": "cpp",
"limits": "cpp",
"locale": "cpp",
"map": "cpp",
"memory": "cpp",
"mutex": "cpp",
"new": "cpp",
"numeric": "cpp",
"ratio": "cpp",
"set": "cpp",
"sstream": "cpp",
"stdexcept": "cpp",
"stop_token": "cpp",
"streambuf": "cpp",
"string": "cpp",
"strstream": "cpp",
"system_error": "cpp",
"thread": "cpp",
"tuple": "cpp",
"type_traits": "cpp",
"typeinfo": "cpp",
"unordered_map": "cpp",
"unordered_set": "cpp",
"utility": "cpp",
"xfacet": "cpp",
"xhash": "cpp",
"xiosbase": "cpp",
"xlocale": "cpp",
"xlocinfo": "cpp",
"xlocmes": "cpp",
"xloctime": "cpp",
"xmemory": "cpp",
"xstddef": "cpp",
"xtr1common": "cpp",
"xutility": "cpp"
}
}

View File

View File

@@ -0,0 +1,31 @@
#include<iostream>
#include<sstream>
#include<vector>
#include<algorithm>
using namespace std;
/*
输入例子1:
a,c,bb
f,dddd
nowcoder
*/
int main(){
string m = "a,c,bb\nf,dddd\nnowcoder";
auto cin=stringstream(m);
string s,t;
while(getline(cin,s)){
vector<string> vec;
auto ss = stringstream(s);
while(getline(ss,t,',')){
vec.push_back(t);
}
sort(vec.begin(),vec.end());
for(int i=0;i<vec.size();i++){
cout<<vec[i];
if(i!=vec.size()-1)cout<<",";
}
cout<<endl;
}
return 0;
}

View File

@@ -0,0 +1,35 @@
#include<iostream>
#include<sstream>
using namespace std;
/*
输入描述:
输入的第一行包括一个正整数t(1 <= t <= 100), 表示数据组数。
接下来t行, 每行一组数据。
每行的第一个整数为整数的个数n(1 <= n <= 100)。
接下来n个正整数, 即需要求和的每个正整数。
输出描述:
每组数据输出求和的结果
输入例子1:
2
4 1 2 3 4
5 1 2 3 4 5
*/
int main(){
string s="2 4 1 2 3 4 5 1 2 3 4 5";
auto cin=stringstream(s);
int t,n,a;
cin>>t;
while(t--){
cin>>n;
int sum=0;
while(n--){
cin>>a;
sum+=a;
}
cout<<sum;
}
return 0;
}

View File

@@ -16,7 +16,8 @@
- [x] 简历投递 join.qq.com
- [x] 2021年4月8日16:00 1面。准备以上内容。
- [x] 2021年4月9日10:30 1面。面试改到北京了
- [ ] 2021-04-13 10:30 2面。面试项目与基础知识
- [x] 2021-04-13 10:30 2面。面试项目与基础知识
- [ ] 04-18 20:00:00 -- 22:00:00 笔试
> 但是TMD之前的面试进度还在。没办法参加第二次面试了早知道直接换个事业群换一波人说不定还好说话。妈卖批。别是上一个boss
## 商汤科技

View File

@@ -3,7 +3,7 @@
> linux实现IO过程的方法有很多。这里只对五种模型进行介绍。如果要实现五种IO过程在linux系统编程部分进行学习。
## 1 I/O 模型
## 0 I/O 模型
* 一个输入操作通常包括两个阶段:
@@ -20,7 +20,7 @@
- 信号驱动式 I/OSIGIO
- 异步 I/OAIO
### 阻塞式 I/O
## 1 阻塞式 I/O
* 应用进程被阻塞,直到数据从内核缓冲区复制到应用进程缓冲区中才返回。
@@ -36,7 +36,7 @@ ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *
![](image/2021-04-06-11-43-08.png)
### 非阻塞式 I/O
## 2 非阻塞式 I/O
* 应用进程执行系统调用之后,内核返回一个错误码。应用进程可以继续执行,但是需要不断的执行系统调用来获知 I/O 是否完成这种方式称为轮询polling
@@ -44,7 +44,7 @@ ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *
![](image/2021-03-30-22-02-42.png)
### I/O 复用事件驱动IO
## 3 I/O 复用事件驱动IO
* I/O多路复用。I/O指的是I/O事件包括I/O读写、I/O异常等事件,多路指多个独立连接(或多个Channel)复用指多个事件复用一个控制流线程或进程。串起来理解就是很多个独立I/O事件的处理依赖于一个控制流。
* 主要是select、poll、epoll对一个IO端口两次调用两次返回比阻塞IO并没有什么优越性关键是能实现同时对**多个IO端口进行监听**
* I/O复用模型会用到select、poll、epoll函数这几个函数也会使进程阻塞但是和阻塞I/O所不同的的这两个函数可以同时阻塞多个I/O操作。而且可以同时对多个读操作多个写操作的I/O函数进行检测直到有数据可读或可写时才真正调用I/O操作函数。当某一个套接字可读时返回之后再使用 recvfrom 把数据从内核复制到进程中。
@@ -54,7 +54,7 @@ ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *
![](image/2021-03-30-22-02-51.png)
### 信号驱动 I/O
## 4 信号驱动 I/O
* 应用进程使用 sigaction 系统调用,内核立即返回,应用进程可以继续执行,也就是说等待数据阶段应用进程是非阻塞的。内核在数据到达时向应用进程发送 SIGIO 信号,应用进程收到之后在信号处理程序中调用 recvfrom 将数据从内核复制到应用进程中。
@@ -64,7 +64,7 @@ ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *
![](image/2021-04-06-11-44-30.png)
### 异步 I/O
## 5 异步 I/O
* 应用进程执行 aio_read 系统调用会立即返回,应用进程可以继续执行,不会被阻塞,内核会在所有操作完成之后向应用进程发送信号。
@@ -73,7 +73,7 @@ ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *
![](image/2021-03-30-22-03-09.png)
![](image/2021-04-06-11-58-05.png)
### 五大 IO 模型比较
## 6 五大 IO 模型比较
- 同步IO将数据从内核缓冲区复制到应用进程缓冲区的阶段第二阶段应用进程会阻塞。包括阻塞式 IO、非阻塞式 IO、IO 复用和信号驱动 IO 。
- 异步IO第一阶段和第二阶段应用进程不会阻塞。
@@ -82,11 +82,13 @@ ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *
![](image/2021-03-30-22-04-01.png)
## 2 I/O 复用实现方式
## 7 I/O 复用实现方式
> select/poll/epoll 都是 I/O 多路复用的具体实现select 出现的最早,之后是 poll再是 epoll。
## 2.1 select
## 7.1 select
### 概述
* select 允许应用程序监视一组文件描述符,等待一个或者多个描述符成为就绪状态,从而完成 I/O 操作。
@@ -152,8 +154,9 @@ else
* 同时每次调用select都需要在内核遍历传递进来的所有fd_set如果fd_set集合很大时那这个开销也很大
* 为了减少数据拷贝带来的性能损坏内核对被监控的fd_set集合大小做了限制并且这个是通过宏控制的大小不可改变(限制为1024)
## 2.2 poll
## 7.2 poll
### 概述
* poll的机制与select类似与select在本质上没有多大差别管理多个描述符也是进行轮询根据描述符的状态进行处理但是poll没有最大文件描述符数量的限制。poll 的功能与 select 类似也是等待一组描述符中的一个成为就绪状态。也就是说poll只解决了上面的问题3并没有解决问题12的性能开销问题。
### 函数说明
@@ -218,7 +221,10 @@ else
3. 可移植性。几乎所有的系统都支持 select但是只有比较新的系统支持 poll。
## 2.3 epoll
## 7.3 epoll
### 概述
* epoll在Linux2.6内核正式提出是基于事件驱动的I/O方式相对于select来说epoll没有描述符个数限制使用一个文件描述符管理多个描述符将用户关心的文件描述符的事件存放到内核的一个事件表中这样在用户空间和内核空间的copy只需一次。
### 函数说明
@@ -327,7 +333,7 @@ else
3. epoll 应用场景。只需要运行在 Linux 平台上,有大量的描述符需要同时轮询,并且这些连接最好是长连接。需要同时监控小于 1000 个描述符,就没有必要使用 epoll因为这个应用场景下并不能体现 epoll 的优势。需要监控的描述符状态变化多,而且都是非常短暂的,也没有必要使用 epoll。因为 epoll 中的所有描述符都存储在内核中,造成每次需要对描述符的状态改变都需要通过 epoll_ctl() 进行系统调用,频繁系统调用降低效率。并且 epoll 的描述符存储在内核,不容易调试。
## 3 对比
## 8 对比
| | select | poll | epoll |
|---|---|---|---|

View File

@@ -0,0 +1,11 @@
## UnixIO模型
![](image/2021-03-30-22-04-01.png)
## WindowsIO模型
![](image/2021-04-15-18-37-05.png)

View File

@@ -0,0 +1,84 @@
## Rector设计模式
### reactor模式结构
![](image/2021-04-15-19-09-12.png)
Reactor包含如下角色
* Handle 句柄用来标识socket连接或是打开文件
* Synchronous Event Demultiplexer同步事件多路分解器由操作系统内核实现的一个函数用于阻塞等待发生在句柄集合上的一个或多个事件如select/epoll
* Event Handler事件处理接口
* Concrete Event HandlerA实现应用程序所提供的特定事件处理逻辑
* Reactor反应器定义一个接口实现以下功能
1. 供应用程序注册和删除关注的事件句柄;
2. 运行事件循环;
3. 有就绪事件到来时,分发事件到之前注册的回调函数上处理;
> “反应”器名字中”反应“的由来:“反应”即“倒置”,“控制逆转”,具体事件处理程序不调用反应器,而是由反应器分配一个具体事件处理程序,具体事件处理程序对某个指定的事件发生做出反应;这种控制逆转又称为“好莱坞法则”(不要调用我,让我来调用你)
### 业务流程及时序图
![](image/2021-04-15-19-09-21.png)
1. 应用启动将关注的事件handle注册到Reactor中
2. 调用Reactor进入无限事件循环等待注册的事件到来
3. 事件到来select返回Reactor将事件分发到之前注册的回调函数中处理
## 2 主动器Proactor
### Proactor模式结构
![](image/2021-04-15-19-09-31.png)
Proactor主动器模式包含如下角色
* Handle 句柄用来标识socket连接或是打开文件
* Asynchronous Operation Processor异步操作处理器负责执行异步操作一般由操作系统内核实现
* Asynchronous Operation异步操作
* Completion Event Queue完成事件队列异步操作完成的结果放到队列中等待后续使用
* Proactor主动器为应用程序进程提供事件循环从完成事件队列中取出异步操作的结果分发调用相应的后续处理逻辑
* Completion Handler完成事件接口一般是由回调函数组成的接口
* Concrete Completion Handler完成事件处理逻辑实现接口定义特定的应用处理逻辑
### 业务流程及时序图
![](image/2021-04-15-19-09-40.png)
1. 应用程序启动,调用异步操作处理器提供的异步操作接口函数,调用之后应用程序和异步操作处理就独立运行;应用程序可以调用新的异步操作,而其它操作可以并发进行;
2. 应用程序启动Proactor主动器进行无限的事件循环等待完成事件到来
3. 异步操作处理器执行异步操作,完成后将结果放入到完成事件队列;
4. 主动器从完成事件队列中取出结果,分发到相应的完成事件回调函数处理逻辑中;
## 3 对比
### 主动和被动
以主动写为例:
1. Reactor将handle放到select()等待可写就绪然后调用write()写入数据;写完处理后续逻辑;
2. Proactor调用aoi_write后立刻返回由内核负责写操作写完后调用相应的回调函数处理后续逻辑
1. 可以看出Reactor被动的等待指示事件的到来并做出反应它有一个等待的过程做什么都要先放入到监听事件集合中等待handler可用时再进行操作
2. Proactor直接调用异步读写操作调用完后立刻返回
### 实现
1. Reactor实现了一个被动的事件分离和分发模型服务等待请求事件的到来再通过不受间断的同步处理事件从而做出反应
2. Proactor实现了一个主动的事件分离和分发模型这种设计允许多个任务并发的执行从而提高吞吐量并可执行耗时长的任务各个任务间互不影响
### 优点
1. Reactor实现相对简单对于耗时短的处理场景处理高效操作系统可以在多个事件源上等待并且避免了多线程编程相关的性能开销和编程复杂性事件的串行化对应用是透明的可以顺序的同步执行而不需要加锁事务分离将与应用无关的多路分解和分配机制和与应用相关的回调函数分离开来
2. Proactor性能更高能够处理耗时长的并发场景
### 缺点
1. Reactor处理耗时长的操作会造成事件分发的阻塞影响到后续事件的处理
2. Proactor实现逻辑复杂依赖操作系统对异步的支持目前实现了纯异步操作的操作系统少实现优秀的如windows IOCP但由于其windows系统用于服务器的局限性目前应用范围较小而Unix/Linux系统对纯异步的支持有限应用事件驱动的主流还是通过select/epoll来实现
### 适用场景
1. Reactor同时接收多个服务请求并且依次同步的处理它们的事件驱动程序
2. Proactor异步接收和同时处理多个服务请求的事件驱动程序

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 152 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 207 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 143 KiB

View File

@@ -228,3 +228,8 @@ int pthread_cond_broadcast(pthread_cond_t *cond);
* 这两个函数用于通知线程条件已经满足. 调用这两个函数, 也称向线程或条件发送信号. 必须注意, 一定要在改变条件状态以后再给线程发送信号.
## 5 管程
## 6 协程

View File

@@ -1,7 +1,5 @@
# 中断
## 1 中断定义
### 广义和狭义

View File

@@ -77,8 +77,8 @@
### 实现并行编程的几种方法
1. java 多线程
2. nodejs 异步回调
3. linux IO复用。epoll事件回调
1. java 多线程
2. nodejs 单线程异步回调。非阻塞异步回调。Proactor模式
3. linux 单线程IO复用。select/poll/epoll事件回调。多路堵塞同步回调。reactor模式
> 这里所谓的什么机制、什么方法。都是设计模式的一部分。通过某种设计模式,实现并行编程:异步回调模式、事件回调模式等。

View File

@@ -0,0 +1,635 @@
<!-- GFM-TOC -->
- [创建型](#创建型)
- [1. 单例Singleton](#1-单例singleton)
- [Intent](#intent)
- [Class Diagram](#class-diagram)
- [Implementation](#implementation)
- [ 懒汉式-线程不安全](#-懒汉式-线程不安全)
- [Ⅱ 饿汉式-线程安全](#ⅱ-饿汉式-线程安全)
- [Ⅲ 懒汉式-线程安全](#ⅲ-懒汉式-线程安全)
- [Ⅳ 双重校验锁-线程安全](#ⅳ-双重校验锁-线程安全)
- [ 静态内部类实现](#-静态内部类实现)
- [Ⅵ 枚举实现](#ⅵ-枚举实现)
- [Examples](#examples)
- [JDK](#jdk)
- [2. 简单工厂Simple Factory](#2-简单工厂simple-factory)
- [Intent](#intent-1)
- [Class Diagram](#class-diagram-1)
- [Implementation](#implementation-1)
- [3. 工厂方法Factory Method](#3-工厂方法factory-method)
- [Intent](#intent-2)
- [Class Diagram](#class-diagram-2)
- [Implementation](#implementation-2)
- [JDK](#jdk-1)
- [4. 抽象工厂Abstract Factory](#4-抽象工厂abstract-factory)
- [Intent](#intent-3)
- [Class Diagram](#class-diagram-3)
- [Implementation](#implementation-3)
- [JDK](#jdk-2)
- [5. 生成器Builder](#5-生成器builder)
- [Intent](#intent-4)
- [Class Diagram](#class-diagram-4)
- [Implementation](#implementation-4)
- [JDK](#jdk-3)
- [6. 原型模式Prototype](#6-原型模式prototype)
- [Intent](#intent-5)
- [Class Diagram](#class-diagram-5)
- [Implementation](#implementation-5)
- [JDK](#jdk-4)
<!-- GFM-TOC -->
> 设计模式是解决问题的方案,学习现有的设计模式可以做到经验复用。拥有设计模式词汇,在沟通时就能用更少的词汇来讨论,并且不需要了解底层细节。
# 创建型
## 1. 单例Singleton
### Intent
确保一个类只有一个实例,并提供该实例的全局访问点。
### Class Diagram
使用一个私有构造函数、一个私有静态变量以及一个公有静态函数来实现。
私有构造函数保证了不能通过构造函数来创建对象实例,只能通过公有静态函数返回唯一的私有静态变量。
![](image/2021-04-10-16-48-05.png)
### Implementation
#### 懒汉式-线程不安全
以下实现中,私有静态变量 uniqueInstance 被延迟实例化,这样做的好处是,如果没有用到该类,那么就不会实例化 uniqueInstance从而节约资源。
这个实现在多线程环境下是不安全的,如果多个线程能够同时进入 `if (uniqueInstance == null)` ,并且此时 uniqueInstance 为 null那么会有多个线程执行 `uniqueInstance = new Singleton();` 语句,这将导致实例化多次 uniqueInstance。
```java
public class Singleton {
private static Singleton uniqueInstance;
private Singleton() {
}
public static Singleton getUniqueInstance() {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
}
```
#### Ⅱ 饿汉式-线程安全
线程不安全问题主要是由于 uniqueInstance 被实例化多次,采取直接实例化 uniqueInstance 的方式就不会产生线程不安全问题。
但是直接实例化的方式也丢失了延迟实例化带来的节约资源的好处。
```java
private static Singleton uniqueInstance = new Singleton();
```
#### Ⅲ 懒汉式-线程安全
只需要对 getUniqueInstance() 方法加锁,那么在一个时间点只能有一个线程能够进入该方法,从而避免了实例化多次 uniqueInstance。
但是当一个线程进入该方法之后,其它试图进入该方法的线程都必须等待,即使 uniqueInstance 已经被实例化了。这会让线程阻塞时间过长,因此该方法有性能问题,不推荐使用。
```java
public static synchronized Singleton getUniqueInstance() {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
```
#### Ⅳ 双重校验锁-线程安全
uniqueInstance 只需要被实例化一次,之后就可以直接使用了。加锁操作只需要对实例化那部分的代码进行,只有当 uniqueInstance 没有被实例化时,才需要进行加锁。
双重校验锁先判断 uniqueInstance 是否已经被实例化,如果没有被实例化,那么才对实例化语句进行加锁。
```java
public class Singleton {
private volatile static Singleton uniqueInstance;
private Singleton() {
}
public static Singleton getUniqueInstance() {
if (uniqueInstance == null) {
synchronized (Singleton.class) {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
}
}
return uniqueInstance;
}
}
```
考虑下面的实现,也就是只使用了一个 if 语句。在 uniqueInstance == null 的情况下,如果两个线程都执行了 if 语句,那么两个线程都会进入 if 语句块内。虽然在 if 语句块内有加锁操作,但是两个线程都会执行 `uniqueInstance = new Singleton();` 这条语句,只是先后的问题,那么就会进行两次实例化。因此必须使用双重校验锁,也就是需要使用两个 if 语句:第一个 if 语句用来避免 uniqueInstance 已经被实例化之后的加锁操作,而第二个 if 语句进行了加锁,所以只能有一个线程进入,就不会出现 uniqueInstance == null 时两个线程同时进行实例化操作。
```java
if (uniqueInstance == null) {
synchronized (Singleton.class) {
uniqueInstance = new Singleton();
}
}
```
uniqueInstance 采用 volatile 关键字修饰也是很有必要的, `uniqueInstance = new Singleton();` 这段代码其实是分为三步执行:
1. 为 uniqueInstance 分配内存空间
2. 初始化 uniqueInstance
3. 将 uniqueInstance 指向分配的内存地址
但是由于 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 实例,并且 JVM 能确保 INSTANCE 只被实例化一次。
这种方式不仅具有延迟初始化的好处,而且由 JVM 提供了对线程安全的支持。
```java
public class Singleton {
private Singleton() {
}
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getUniqueInstance() {
return SingletonHolder.INSTANCE;
}
}
```
#### Ⅵ 枚举实现
```java
public enum Singleton {
INSTANCE;
private String objName;
public String getObjName() {
return objName;
}
public void setObjName(String objName) {
this.objName = objName;
}
public static void main(String[] args) {
// 单例测试
Singleton firstSingleton = Singleton.INSTANCE;
firstSingleton.setObjName("firstName");
System.out.println(firstSingleton.getObjName());
Singleton secondSingleton = Singleton.INSTANCE;
secondSingleton.setObjName("secondName");
System.out.println(firstSingleton.getObjName());
System.out.println(secondSingleton.getObjName());
// 反射获取实例测试
try {
Singleton[] enumConstants = Singleton.class.getEnumConstants();
for (Singleton enumConstant : enumConstants) {
System.out.println(enumConstant.getObjName());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
```
```html
firstName
secondName
secondName
secondName
```
该实现可以防止反射攻击。在其它实现中,通过 setAccessible() 方法可以将私有构造函数的访问级别设置为 public然后调用构造函数从而实例化对象如果要防止这种攻击需要在构造函数中添加防止多次实例化的代码。该实现是由 JVM 保证只会实例化一次,因此不会出现上述的反射攻击。
该实现在多次序列化和序列化之后,不会得到多个实例。而其它实现需要使用 transient 修饰所有字段,并且实现序列化和反序列化的方法。
### Examples
- Logger Classes
- Configuration Classes
- Accesing resources in shared mode
- Factories implemented as Singletons
### JDK
- [java.lang.Runtime#getRuntime()](http://docs.oracle.com/javase/8/docs/api/java/lang/Runtime.html#getRuntime%28%29)
- [java.awt.Desktop#getDesktop()](http://docs.oracle.com/javase/8/docs/api/java/awt/Desktop.html#getDesktop--)
- [java.lang.System#getSecurityManager()](http://docs.oracle.com/javase/8/docs/api/java/lang/System.html#getSecurityManager--)
## 2. 简单工厂Simple Factory
### Intent
在创建一个对象时不向客户暴露内部细节,并提供一个创建对象的通用接口。
### Class Diagram
简单工厂把实例化的操作单独放到一个类中,这个类就成为简单工厂类,让简单工厂类来决定应该用哪个具体子类来实例化。
这样做能把客户类和具体子类的实现解耦,客户类不再需要知道有哪些子类以及应当实例化哪个子类。客户类往往有多个,如果不使用简单工厂,那么所有的客户类都要知道所有子类的细节。而且一旦子类发生改变,例如增加子类,那么所有的客户类都要进行修改。
![](image/2021-04-10-15-34-10.png)
### Implementation
```java
public interface Product {
}
```
```java
public class ConcreteProduct implements Product {
}
```
```java
public class ConcreteProduct1 implements Product {
}
```
```java
public class ConcreteProduct2 implements Product {
}
```
以下的 Client 类包含了实例化的代码,这是一种错误的实现。如果在客户类中存在这种实例化代码,就需要考虑将代码放到简单工厂中。
```java
public class Client {
public static void main(String[] args) {
int type = 1;
Product product;
if (type == 1) {
product = new ConcreteProduct1();
} else if (type == 2) {
product = new ConcreteProduct2();
} else {
product = new ConcreteProduct();
}
// do something with the product
}
}
```
以下的 SimpleFactory 是简单工厂实现,它被所有需要进行实例化的客户类调用。
```java
public class SimpleFactory {
public Product createProduct(int type) {
if (type == 1) {
return new ConcreteProduct1();
} else if (type == 2) {
return new ConcreteProduct2();
}
return new ConcreteProduct();
}
}
```
```java
public class Client {
public static void main(String[] args) {
SimpleFactory simpleFactory = new SimpleFactory();
Product product = simpleFactory.createProduct(1);
// do something with the product
}
}
```
## 3. 工厂方法Factory Method
### Intent
定义了一个创建对象的接口,但由子类决定要实例化哪个类。工厂方法把实例化操作推迟到子类。
### Class Diagram
在简单工厂中,创建对象的是另一个类,而在工厂方法中,是由子类来创建对象。
下图中Factory 有一个 doSomething() 方法,这个方法需要用到一个产品对象,这个产品对象由 factoryMethod() 方法创建。该方法是抽象的,需要由子类去实现。
![](image/2021-04-10-15-32-27.png)
### Implementation
```java
public abstract class Factory {
abstract public Product factoryMethod();
public void doSomething() {
Product product = factoryMethod();
// do something with the product
}
}
```
```java
public class ConcreteFactory extends Factory {
public Product factoryMethod() {
return new ConcreteProduct();
}
}
```
```java
public class ConcreteFactory1 extends Factory {
public Product factoryMethod() {
return new ConcreteProduct1();
}
}
```
```java
public class ConcreteFactory2 extends Factory {
public Product factoryMethod() {
return new ConcreteProduct2();
}
}
```
### JDK
- [java.util.Calendar](http://docs.oracle.com/javase/8/docs/api/java/util/Calendar.html#getInstance--)
- [java.util.ResourceBundle](http://docs.oracle.com/javase/8/docs/api/java/util/ResourceBundle.html#getBundle-java.lang.String-)
- [java.text.NumberFormat](http://docs.oracle.com/javase/8/docs/api/java/text/NumberFormat.html#getInstance--)
- [java.nio.charset.Charset](http://docs.oracle.com/javase/8/docs/api/java/nio/charset/Charset.html#forName-java.lang.String-)
- [java.net.URLStreamHandlerFactory](http://docs.oracle.com/javase/8/docs/api/java/net/URLStreamHandlerFactory.html#createURLStreamHandler-java.lang.String-)
- [java.util.EnumSet](https://docs.oracle.com/javase/8/docs/api/java/util/EnumSet.html#of-E-)
- [javax.xml.bind.JAXBContext](https://docs.oracle.com/javase/8/docs/api/javax/xml/bind/JAXBContext.html#createMarshaller--)
## 4. 抽象工厂Abstract Factory
### Intent
提供一个接口,用于创建 **相关的对象家族**
### Class Diagram
抽象工厂模式创建的是对象家族,也就是很多对象而不是一个对象,并且这些对象是相关的,也就是说必须一起创建出来。而工厂方法模式只是用于创建一个对象,这和抽象工厂模式有很大不同。
抽象工厂模式用到了工厂方法模式来创建单一对象AbstractFactory 中的 createProductA() 和 createProductB() 方法都是让子类来实现,这两个方法单独来看就是在创建一个对象,这符合工厂方法模式的定义。
至于创建对象的家族这一概念是在 Client 体现Client 要通过 AbstractFactory 同时调用两个方法来创建出两个对象在这里这两个对象就有很大的相关性Client 需要同时创建出这两个对象。
从高层次来看,抽象工厂使用了组合,即 Cilent 组合了 AbstractFactory而工厂方法模式使用了继承。
![](image/2021-04-10-15-26-15.png)
### Implementation
```java
public class AbstractProductA {
}
```
```java
public class AbstractProductB {
}
```
```java
public class ProductA1 extends AbstractProductA {
}
```
```java
public class ProductA2 extends AbstractProductA {
}
```
```java
public class ProductB1 extends AbstractProductB {
}
```
```java
public class ProductB2 extends AbstractProductB {
}
```
```java
public abstract class AbstractFactory {
abstract AbstractProductA createProductA();
abstract AbstractProductB createProductB();
}
```
```java
public class ConcreteFactory1 extends AbstractFactory {
AbstractProductA createProductA() {
return new ProductA1();
}
AbstractProductB createProductB() {
return new ProductB1();
}
}
```
```java
public class ConcreteFactory2 extends AbstractFactory {
AbstractProductA createProductA() {
return new ProductA2();
}
AbstractProductB createProductB() {
return new ProductB2();
}
}
```
```java
public class Client {
public static void main(String[] args) {
AbstractFactory abstractFactory = new ConcreteFactory1();
AbstractProductA productA = abstractFactory.createProductA();
AbstractProductB productB = abstractFactory.createProductB();
// do something with productA and productB
}
}
```
### JDK
- [javax.xml.parsers.DocumentBuilderFactory](http://docs.oracle.com/javase/8/docs/api/javax/xml/parsers/DocumentBuilderFactory.html)
- [javax.xml.transform.TransformerFactory](http://docs.oracle.com/javase/8/docs/api/javax/xml/transform/TransformerFactory.html#newInstance--)
- [javax.xml.xpath.XPathFactory](http://docs.oracle.com/javase/8/docs/api/javax/xml/xpath/XPathFactory.html#newInstance--)
## 5. 生成器Builder
### Intent
封装一个对象的构造过程,并允许按步骤构造。
### Class Diagram
![](image/2021-04-10-15-39-40.png)
### Implementation
以下是一个简易的 StringBuilder 实现,参考了 JDK 1.8 源码。
```java
public class AbstractStringBuilder {
protected char[] value;
protected int count;
public AbstractStringBuilder(int capacity) {
count = 0;
value = new char[capacity];
}
public AbstractStringBuilder append(char c) {
ensureCapacityInternal(count + 1);
value[count++] = c;
return this;
}
private void ensureCapacityInternal(int minimumCapacity) {
// overflow-conscious code
if (minimumCapacity - value.length > 0)
expandCapacity(minimumCapacity);
}
void expandCapacity(int minimumCapacity) {
int newCapacity = value.length * 2 + 2;
if (newCapacity - minimumCapacity < 0)
newCapacity = minimumCapacity;
if (newCapacity < 0) {
if (minimumCapacity < 0) // overflow
throw new OutOfMemoryError();
newCapacity = Integer.MAX_VALUE;
}
value = Arrays.copyOf(value, newCapacity);
}
}
```
```java
public class StringBuilder extends AbstractStringBuilder {
public StringBuilder() {
super(16);
}
@Override
public String toString() {
// Create a copy, don't share the array
return new String(value, 0, count);
}
}
```
```java
public class Client {
public static void main(String[] args) {
StringBuilder sb = new StringBuilder();
final int count = 26;
for (int i = 0; i < count; i++) {
sb.append((char) ('a' + i));
}
System.out.println(sb.toString());
}
}
```
```html
abcdefghijklmnopqrstuvwxyz
```
### JDK
- [java.lang.StringBuilder](http://docs.oracle.com/javase/8/docs/api/java/lang/StringBuilder.html)
- [java.nio.ByteBuffer](http://docs.oracle.com/javase/8/docs/api/java/nio/ByteBuffer.html#put-byte-)
- [java.lang.StringBuffer](http://docs.oracle.com/javase/8/docs/api/java/lang/StringBuffer.html#append-boolean-)
- [java.lang.Appendable](http://docs.oracle.com/javase/8/docs/api/java/lang/Appendable.html)
- [Apache Camel builders](https://github.com/apache/camel/tree/0e195428ee04531be27a0b659005e3aa8d159d23/camel-core/src/main/java/org/apache/camel/builder)
## 6. 原型模式Prototype
### Intent
使用原型实例指定要创建对象的类型,通过复制这个原型来创建新对象。
### Class Diagram
![](image/2021-04-10-15-44-20.png)
### Implementation
```java
public abstract class Prototype {
abstract Prototype myClone();
}
```
```java
public class ConcretePrototype extends Prototype {
private String filed;
public ConcretePrototype(String filed) {
this.filed = filed;
}
@Override
Prototype myClone() {
return new ConcretePrototype(filed);
}
@Override
public String toString() {
return filed;
}
}
```
```java
public class Client {
public static void main(String[] args) {
Prototype prototype = new ConcretePrototype("abc");
Prototype clone = prototype.myClone();
System.out.println(clone.toString());
}
}
```
```html
abc
```
### JDK
- [java.lang.Object#clone()](http://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#clone%28%29)

View File

@@ -0,0 +1,777 @@
<!-- GFM-TOC -->
- [二、结构型](#二结构型)
- [1. 适配器Adapter](#1-适配器adapter)
- [Intent](#intent)
- [Class Diagram](#class-diagram)
- [Implementation](#implementation)
- [JDK](#jdk)
- [2. 桥接Bridge](#2-桥接bridge)
- [Intent](#intent-1)
- [Class Diagram](#class-diagram-1)
- [Implementation](#implementation-1)
- [JDK](#jdk-1)
- [3. 组合Composite](#3-组合composite)
- [Intent](#intent-2)
- [Class Diagram](#class-diagram-2)
- [Implementation](#implementation-2)
- [JDK](#jdk-2)
- [4. 装饰Decorator](#4-装饰decorator)
- [Intent](#intent-3)
- [Class Diagram](#class-diagram-3)
- [Implementation](#implementation-3)
- [设计原则](#设计原则)
- [JDK](#jdk-3)
- [5. 外观Facade](#5-外观facade)
- [Intent](#intent-4)
- [Class Diagram](#class-diagram-4)
- [Implementation](#implementation-4)
- [设计原则](#设计原则-1)
- [6. 享元Flyweight](#6-享元flyweight)
- [Intent](#intent-5)
- [Class Diagram](#class-diagram-5)
- [Implementation](#implementation-5)
- [JDK](#jdk-4)
- [7. 代理Proxy](#7-代理proxy)
- [Intent](#intent-6)
- [Class Diagram](#class-diagram-6)
- [Implementation](#implementation-6)
- [JDK](#jdk-5)
- [参考资料](#参考资料)
<!-- GFM-TOC -->
> 设计模式是解决问题的方案,学习现有的设计模式可以做到经验复用。拥有设计模式词汇,在沟通时就能用更少的词汇来讨论,并且不需要了解底层细节。
# 二、结构型
## 1. 适配器Adapter
### Intent
把一个类接口转换成另一个用户需要的接口。
![](image/2021-04-10-15-41-07.png)
### Class Diagram
![](image/2021-04-10-15-40-27.png)
### Implementation
鸭子Duck和火鸡Turkey拥有不同的叫声Duck 的叫声调用 quack() 方法,而 Turkey 调用 gobble() 方法。
要求将 Turkey 的 gobble() 方法适配成 Duck 的 quack() 方法,从而让火鸡冒充鸭子!
```java
public interface Duck {
void quack();
}
```
```java
public interface Turkey {
void gobble();
}
```
```java
public class WildTurkey implements Turkey {
@Override
public void gobble() {
System.out.println("gobble!");
}
}
```
```java
public class TurkeyAdapter implements Duck {
Turkey turkey;
public TurkeyAdapter(Turkey turkey) {
this.turkey = turkey;
}
@Override
public void quack() {
turkey.gobble();
}
}
```
```java
public class Client {
public static void main(String[] args) {
Turkey turkey = new WildTurkey();
Duck duck = new TurkeyAdapter(turkey);
duck.quack();
}
}
```
### JDK
- [java.util.Arrays#asList()](http://docs.oracle.com/javase/8/docs/api/java/util/Arrays.html#asList%28T...%29)
- [java.util.Collections#list()](https://docs.oracle.com/javase/8/docs/api/java/util/Collections.html#list-java.util.Enumeration-)
- [java.util.Collections#enumeration()](https://docs.oracle.com/javase/8/docs/api/java/util/Collections.html#enumeration-java.util.Collection-)
- [javax.xml.bind.annotation.adapters.XMLAdapter](http://docs.oracle.com/javase/8/docs/api/javax/xml/bind/annotation/adapters/XmlAdapter.html#marshal-BoundType-)
## 2. 桥接Bridge
### Intent
将抽象与实现分离开来,使它们可以独立变化。
### Class Diagram
- Abstraction定义抽象类的接口
- Implementor定义实现类接口
![](image/2021-04-10-15-38-25.png)
### Implementation
RemoteControl 表示遥控器,指代 Abstraction。
TV 表示电视,指代 Implementor。
桥接模式将遥控器和电视分离开来,从而可以独立改变遥控器或者电视的实现。
```java
public abstract class TV {
public abstract void on();
public abstract void off();
public abstract void tuneChannel();
}
```
```java
public class Sony extends TV {
@Override
public void on() {
System.out.println("Sony.on()");
}
@Override
public void off() {
System.out.println("Sony.off()");
}
@Override
public void tuneChannel() {
System.out.println("Sony.tuneChannel()");
}
}
```
```java
public class RCA extends TV {
@Override
public void on() {
System.out.println("RCA.on()");
}
@Override
public void off() {
System.out.println("RCA.off()");
}
@Override
public void tuneChannel() {
System.out.println("RCA.tuneChannel()");
}
}
```
```java
public abstract class RemoteControl {
protected TV tv;
public RemoteControl(TV tv) {
this.tv = tv;
}
public abstract void on();
public abstract void off();
public abstract void tuneChannel();
}
```
```java
public class ConcreteRemoteControl1 extends RemoteControl {
public ConcreteRemoteControl1(TV tv) {
super(tv);
}
@Override
public void on() {
System.out.println("ConcreteRemoteControl1.on()");
tv.on();
}
@Override
public void off() {
System.out.println("ConcreteRemoteControl1.off()");
tv.off();
}
@Override
public void tuneChannel() {
System.out.println("ConcreteRemoteControl1.tuneChannel()");
tv.tuneChannel();
}
}
```
```java
public class ConcreteRemoteControl2 extends RemoteControl {
public ConcreteRemoteControl2(TV tv) {
super(tv);
}
@Override
public void on() {
System.out.println("ConcreteRemoteControl2.on()");
tv.on();
}
@Override
public void off() {
System.out.println("ConcreteRemoteControl2.off()");
tv.off();
}
@Override
public void tuneChannel() {
System.out.println("ConcreteRemoteControl2.tuneChannel()");
tv.tuneChannel();
}
}
```
```java
public class Client {
public static void main(String[] args) {
RemoteControl remoteControl1 = new ConcreteRemoteControl1(new RCA());
remoteControl1.on();
remoteControl1.off();
remoteControl1.tuneChannel();
RemoteControl remoteControl2 = new ConcreteRemoteControl2(new Sony());
remoteControl2.on();
remoteControl2.off();
remoteControl2.tuneChannel();
}
}
```
### JDK
- AWT (It provides an abstraction layer which maps onto the native OS the windowing support.)
- JDBC
## 3. 组合Composite
### Intent
将对象组合成树形结构来表示“整体/部分”层次关系,允许用户以相同的方式处理单独对象和组合对象。
### Class Diagram
组件Component类是组合类Composite和叶子类Leaf的父类可以把组合类看成是树的中间节点。
组合对象拥有一个或者多个组件对象,因此组合对象的操作可以委托给组件对象去处理,而组件对象可以是另一个组合对象或者叶子对象。
![](image/2021-04-10-15-48-55.png)
### Implementation
```java
public abstract class Component {
protected String name;
public Component(String name) {
this.name = name;
}
public void print() {
print(0);
}
abstract void print(int level);
abstract public void add(Component component);
abstract public void remove(Component component);
}
```
```java
public class Composite extends Component {
private List<Component> child;
public Composite(String name) {
super(name);
child = new ArrayList<>();
}
@Override
void print(int level) {
for (int i = 0; i < level; i++) {
System.out.print("--");
}
System.out.println("Composite:" + name);
for (Component component : child) {
component.print(level + 1);
}
}
@Override
public void add(Component component) {
child.add(component);
}
@Override
public void remove(Component component) {
child.remove(component);
}
}
```
```java
public class Leaf extends Component {
public Leaf(String name) {
super(name);
}
@Override
void print(int level) {
for (int i = 0; i < level; i++) {
System.out.print("--");
}
System.out.println("left:" + name);
}
@Override
public void add(Component component) {
throw new UnsupportedOperationException(); // 牺牲透明性换取单一职责原则,这样就不用考虑是叶子节点还是组合节点
}
@Override
public void remove(Component component) {
throw new UnsupportedOperationException();
}
}
```
```java
public class Client {
public static void main(String[] args) {
Composite root = new Composite("root");
Component node1 = new Leaf("1");
Component node2 = new Composite("2");
Component node3 = new Leaf("3");
root.add(node1);
root.add(node2);
root.add(node3);
Component node21 = new Leaf("21");
Component node22 = new Composite("22");
node2.add(node21);
node2.add(node22);
Component node221 = new Leaf("221");
node22.add(node221);
root.print();
}
}
```
```html
Composite:root
--left:1
--Composite:2
----left:21
----Composite:22
------left:221
--left:3
```
### JDK
- javax.swing.JComponent#add(Component)
- java.awt.Container#add(Component)
- java.util.Map#putAll(Map)
- java.util.List#addAll(Collection)
- java.util.Set#addAll(Collection)
## 4. 装饰Decorator
### Intent
为对象动态添加功能。
### Class Diagram
装饰者Decorator和具体组件ConcreteComponent都继承自组件Component具体组件的方法实现不需要依赖于其它对象而装饰者组合了一个组件这样它可以装饰其它装饰者或者具体组件。所谓装饰就是把这个装饰者套在被装饰者之上从而动态扩展被装饰者的功能。装饰者的方法有一部分是自己的这属于它的功能然后调用被装饰者的方法实现从而也保留了被装饰者的功能。可以看到具体组件应当是装饰层次的最低层因为只有具体组件的方法实现不需要依赖于其它对象。
![](image/2021-04-10-15-47-29.png)
### Implementation
设计不同种类的饮料,饮料可以添加配料,比如可以添加牛奶,并且支持动态添加新配料。每增加一种配料,该饮料的价格就会增加,要求计算一种饮料的价格。
下图表示在 DarkRoast 饮料上新增新添加 Mocha 配料,之后又添加了 Whip 配料。DarkRoast 被 Mocha 包裹Mocha 又被 Whip 包裹。它们都继承自相同父类,都有 cost() 方法,外层类的 cost() 方法调用了内层类的 cost() 方法。
![](image/2021-04-10-15-47-08.png)
```java
public interface Beverage {
double cost();
}
```
```java
public class DarkRoast implements Beverage {
@Override
public double cost() {
return 1;
}
}
```
```java
public class HouseBlend implements Beverage {
@Override
public double cost() {
return 1;
}
}
```
```java
public abstract class CondimentDecorator implements Beverage {
protected Beverage beverage;
}
```
```java
public class Milk extends CondimentDecorator {
public Milk(Beverage beverage) {
this.beverage = beverage;
}
@Override
public double cost() {
return 1 + beverage.cost();
}
}
```
```java
public class Mocha extends CondimentDecorator {
public Mocha(Beverage beverage) {
this.beverage = beverage;
}
@Override
public double cost() {
return 1 + beverage.cost();
}
}
```
```java
public class Client {
public static void main(String[] args) {
Beverage beverage = new HouseBlend();
beverage = new Mocha(beverage);
beverage = new Milk(beverage);
System.out.println(beverage.cost());
}
}
```
```html
3.0
```
### 设计原则
类应该对扩展开放,对修改关闭:也就是添加新功能时不需要修改代码。饮料可以动态添加新的配料,而不需要去修改饮料的代码。
不可能把所有的类设计成都满足这一原则,应当把该原则应用于最有可能发生改变的地方。
### JDK
- java.io.BufferedInputStream(InputStream)
- java.io.DataInputStream(InputStream)
- java.io.BufferedOutputStream(OutputStream)
- java.util.zip.ZipOutputStream(OutputStream)
- java.util.Collections#checked[List|Map|Set|SortedSet|SortedMap]()
## 5. 外观Facade
### Intent
提供了一个统一的接口,用来访问子系统中的一群接口,从而让子系统更容易使用。
### Class Diagram
![](image/2021-04-10-15-42-05.png)
### Implementation
观看电影需要操作很多电器,使用外观模式实现一键看电影功能。
```java
public class SubSystem {
public void turnOnTV() {
System.out.println("turnOnTV()");
}
public void setCD(String cd) {
System.out.println("setCD( " + cd + " )");
}
public void startWatching(){
System.out.println("startWatching()");
}
}
```
```java
public class Facade {
private SubSystem subSystem = new SubSystem();
public void watchMovie() {
subSystem.turnOnTV();
subSystem.setCD("a movie");
subSystem.startWatching();
}
}
```
```java
public class Client {
public static void main(String[] args) {
Facade facade = new Facade();
facade.watchMovie();
}
}
```
### 设计原则
最少知识原则:只和你的密友谈话。也就是说客户对象所需要交互的对象应当尽可能少。
## 6. 享元Flyweight
### Intent
利用共享的方式来支持大量细粒度的对象,这些对象一部分内部状态是相同的。
### Class Diagram
- Flyweight享元对象
- IntrinsicState内部状态享元对象共享内部状态
- ExtrinsicState外部状态每个享元对象的外部状态不同
![](image/2021-04-10-15-43-43.png)
### Implementation
```java
public interface Flyweight {
void doOperation(String extrinsicState);
}
```
```java
public class ConcreteFlyweight implements Flyweight {
private String intrinsicState;
public ConcreteFlyweight(String intrinsicState) {
this.intrinsicState = intrinsicState;
}
@Override
public void doOperation(String extrinsicState) {
System.out.println("Object address: " + System.identityHashCode(this));
System.out.println("IntrinsicState: " + intrinsicState);
System.out.println("ExtrinsicState: " + extrinsicState);
}
}
```
```java
public class FlyweightFactory {
private HashMap<String, Flyweight> flyweights = new HashMap<>();
Flyweight getFlyweight(String intrinsicState) {
if (!flyweights.containsKey(intrinsicState)) {
Flyweight flyweight = new ConcreteFlyweight(intrinsicState);
flyweights.put(intrinsicState, flyweight);
}
return flyweights.get(intrinsicState);
}
}
```
```java
public class Client {
public static void main(String[] args) {
FlyweightFactory factory = new FlyweightFactory();
Flyweight flyweight1 = factory.getFlyweight("aa");
Flyweight flyweight2 = factory.getFlyweight("aa");
flyweight1.doOperation("x");
flyweight2.doOperation("y");
}
}
```
```html
Object address: 1163157884
IntrinsicState: aa
ExtrinsicState: x
Object address: 1163157884
IntrinsicState: aa
ExtrinsicState: y
```
### JDK
Java 利用缓存来加速大量小对象的访问时间。
- java.lang.Integer#valueOf(int)
- java.lang.Boolean#valueOf(boolean)
- java.lang.Byte#valueOf(byte)
- java.lang.Character#valueOf(char)
## 7. 代理Proxy
### Intent
控制对其它对象的访问。
### Class Diagram
代理有以下四类:
- 远程代理Remote Proxy控制对远程对象不同地址空间的访问它负责将请求及其参数进行编码并向不同地址空间中的对象发送已经编码的请求。
- 虚拟代理Virtual Proxy根据需要创建开销很大的对象它可以缓存实体的附加信息以便延迟对它的访问例如在网站加载一个很大图片时不能马上完成可以用虚拟代理缓存图片的大小信息然后生成一张临时图片代替原始图片。
- 保护代理Protection Proxy按权限控制对象的访问它负责检查调用者是否具有实现一个请求所必须的访问权限。
- 智能代理Smart Reference取代了简单的指针它在访问对象时执行一些附加操作记录对象的引用次数当第一次引用一个对象时将它装入内存在访问一个实际对象前检查是否已经锁定了它以确保其它对象不能改变它。
![](image/2021-04-10-15-28-02.png)
### Implementation
以下是一个虚拟代理的实现,模拟了图片延迟加载的情况下使用与图片大小相等的临时内容去替换原始图片,直到图片加载完成才将图片显示出来。
```java
public interface Image {
void showImage();
}
```
```java
public class HighResolutionImage implements Image {
private URL imageURL;
private long startTime;
private int height;
private int width;
public int getHeight() {
return height;
}
public int getWidth() {
return width;
}
public HighResolutionImage(URL imageURL) {
this.imageURL = imageURL;
this.startTime = System.currentTimeMillis();
this.width = 600;
this.height = 600;
}
public boolean isLoad() {
// 模拟图片加载,延迟 3s 加载完成
long endTime = System.currentTimeMillis();
return endTime - startTime > 3000;
}
@Override
public void showImage() {
System.out.println("Real Image: " + imageURL);
}
}
```
```java
public class ImageProxy implements Image {
private HighResolutionImage highResolutionImage;
public ImageProxy(HighResolutionImage highResolutionImage) {
this.highResolutionImage = highResolutionImage;
}
@Override
public void showImage() {
while (!highResolutionImage.isLoad()) {
try {
System.out.println("Temp Image: " + highResolutionImage.getWidth() + " " + highResolutionImage.getHeight());
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
highResolutionImage.showImage();
}
}
```
```java
public class ImageViewer {
public static void main(String[] args) throws Exception {
String image = "http://image.jpg";
URL url = new URL(image);
HighResolutionImage highResolutionImage = new HighResolutionImage(url);
ImageProxy imageProxy = new ImageProxy(highResolutionImage);
imageProxy.showImage();
}
}
```
### JDK
- java.lang.reflect.Proxy
- RMI
# 参考资料
- 弗里曼. Head First 设计模式 [M]. 中国电力出版社, 2007.
- Gamma E. 设计模式: 可复用面向对象软件的基础 [M]. 机械工业出版社, 2007.
- Bloch J. Effective java[M]. Addison-Wesley Professional, 2017.
- [Design Patterns](http://www.oodesign.com/)
- [Design patterns implemented in Java](http://java-design-patterns.com/)
- [The breakdown of design patterns in JDK](http://www.programering.com/a/MTNxAzMwATY.html)

View File

@@ -0,0 +1,83 @@
## Rector设计模式
### reactor模式结构
![](image/2021-04-15-18-54-18.png)
Reactor包含如下角色
* Handle 句柄用来标识socket连接或是打开文件
* Synchronous Event Demultiplexer同步事件多路分解器由操作系统内核实现的一个函数用于阻塞等待发生在句柄集合上的一个或多个事件如select/epoll
* Event Handler事件处理接口
* Concrete Event HandlerA实现应用程序所提供的特定事件处理逻辑
* Reactor反应器定义一个接口实现以下功能
1. 供应用程序注册和删除关注的事件句柄;
2. 运行事件循环;
3. 有就绪事件到来时,分发事件到之前注册的回调函数上处理;
> “反应”器名字中”反应“的由来:“反应”即“倒置”,“控制逆转”,具体事件处理程序不调用反应器,而是由反应器分配一个具体事件处理程序,具体事件处理程序对某个指定的事件发生做出反应;这种控制逆转又称为“好莱坞法则”(不要调用我,让我来调用你)
### 业务流程及时序图
![](image/2021-04-15-18-59-23.png)
1. 应用启动将关注的事件handle注册到Reactor中
2. 调用Reactor进入无限事件循环等待注册的事件到来
3. 事件到来select返回Reactor将事件分发到之前注册的回调函数中处理
## 2 主动器Proactor
### Proactor模式结构
![](image/2021-04-15-19-00-27.png)
Proactor主动器模式包含如下角色
* Handle 句柄用来标识socket连接或是打开文件
* Asynchronous Operation Processor异步操作处理器负责执行异步操作一般由操作系统内核实现
* Asynchronous Operation异步操作
* Completion Event Queue完成事件队列异步操作完成的结果放到队列中等待后续使用
* Proactor主动器为应用程序进程提供事件循环从完成事件队列中取出异步操作的结果分发调用相应的后续处理逻辑
* Completion Handler完成事件接口一般是由回调函数组成的接口
* Concrete Completion Handler完成事件处理逻辑实现接口定义特定的应用处理逻辑
### 业务流程及时序图
![](image/2021-04-15-19-04-13.png)
1. 应用程序启动,调用异步操作处理器提供的异步操作接口函数,调用之后应用程序和异步操作处理就独立运行;应用程序可以调用新的异步操作,而其它操作可以并发进行;
2. 应用程序启动Proactor主动器进行无限的事件循环等待完成事件到来
3. 异步操作处理器执行异步操作,完成后将结果放入到完成事件队列;
4. 主动器从完成事件队列中取出结果,分发到相应的完成事件回调函数处理逻辑中;
## 3 对比
### 主动和被动
以主动写为例:
1. Reactor将handle放到select()等待可写就绪然后调用write()写入数据;写完处理后续逻辑;
2. Proactor调用aoi_write后立刻返回由内核负责写操作写完后调用相应的回调函数处理后续逻辑
1. 可以看出Reactor被动的等待指示事件的到来并做出反应它有一个等待的过程做什么都要先放入到监听事件集合中等待handler可用时再进行操作
2. Proactor直接调用异步读写操作调用完后立刻返回
### 实现
1. Reactor实现了一个被动的事件分离和分发模型服务等待请求事件的到来再通过不受间断的同步处理事件从而做出反应
2. Proactor实现了一个主动的事件分离和分发模型这种设计允许多个任务并发的执行从而提高吞吐量并可执行耗时长的任务各个任务间互不影响
### 优点
1. Reactor实现相对简单对于耗时短的处理场景处理高效操作系统可以在多个事件源上等待并且避免了多线程编程相关的性能开销和编程复杂性事件的串行化对应用是透明的可以顺序的同步执行而不需要加锁事务分离将与应用无关的多路分解和分配机制和与应用相关的回调函数分离开来
2. Proactor性能更高能够处理耗时长的并发场景
### 缺点
1. Reactor处理耗时长的操作会造成事件分发的阻塞影响到后续事件的处理
2. Proactor实现逻辑复杂依赖操作系统对异步的支持目前实现了纯异步操作的操作系统少实现优秀的如windows IOCP但由于其windows系统用于服务器的局限性目前应用范围较小而Unix/Linux系统对纯异步的支持有限应用事件驱动的主流还是通过select/epoll来实现
### 适用场景
1. Reactor同时接收多个服务请求并且依次同步的处理它们的事件驱动程序
2. Proactor异步接收和同时处理多个服务请求的事件驱动程序

Binary file not shown.

After

Width:  |  Height:  |  Size: 152 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 207 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 143 KiB