设计模式
65
.vscode/settings.json
vendored
@@ -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"
|
||||
}
|
||||
}
|
||||
31
C++/代码片段/test_delim.cpp
Normal 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;
|
||||
}
|
||||
35
C++/代码片段/test_input.cpp
Normal 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;
|
||||
}
|
||||
@@ -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
|
||||
|
||||
## 商汤科技
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
> linux实现IO过程的方法有很多。这里只对五种模型进行介绍。如果要实现五种IO过程,在linux系统编程部分进行学习。
|
||||
|
||||
|
||||
## 1 I/O 模型
|
||||
## 0 I/O 模型
|
||||
|
||||
* 一个输入操作通常包括两个阶段:
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
- 信号驱动式 I/O(SIGIO)
|
||||
- 异步 I/O(AIO)
|
||||
|
||||
### 阻塞式 I/O
|
||||
## 1 阻塞式 I/O
|
||||
|
||||
* 应用进程被阻塞,直到数据从内核缓冲区复制到应用进程缓冲区中才返回。
|
||||
|
||||
@@ -36,7 +36,7 @@ ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *
|
||||
|
||||
|
||||

|
||||
### 非阻塞式 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 *
|
||||
|
||||

|
||||
|
||||
### 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 *
|
||||
|
||||

|
||||
|
||||
### 信号驱动 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 *
|
||||
|
||||

|
||||
|
||||
### 异步 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 *
|
||||

|
||||
|
||||

|
||||
### 五大 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 *
|
||||

|
||||
|
||||
|
||||
## 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,并没有解决问题1,2的性能开销问题。
|
||||
|
||||
### 函数说明
|
||||
@@ -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 |
|
||||
|---|---|---|---|
|
||||
11
操作系统/5.2 Windows IO模型.md
Normal file
@@ -0,0 +1,11 @@
|
||||
|
||||
## UnixIO模型
|
||||
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
## WindowsIO模型
|
||||
|
||||

|
||||
84
操作系统/5.3 IO设计模式.md
Normal file
@@ -0,0 +1,84 @@
|
||||
|
||||
|
||||
## Rector设计模式
|
||||
|
||||
### reactor模式结构
|
||||
|
||||

|
||||
Reactor包含如下角色:
|
||||
|
||||
* Handle 句柄;用来标识socket连接或是打开文件;
|
||||
* Synchronous Event Demultiplexer:同步事件多路分解器:由操作系统内核实现的一个函数;用于阻塞等待发生在句柄集合上的一个或多个事件;(如select/epoll;)
|
||||
* Event Handler:事件处理接口
|
||||
* Concrete Event HandlerA:实现应用程序所提供的特定事件处理逻辑;
|
||||
* Reactor:反应器,定义一个接口,实现以下功能:
|
||||
1. 供应用程序注册和删除关注的事件句柄;
|
||||
2. 运行事件循环;
|
||||
3. 有就绪事件到来时,分发事件到之前注册的回调函数上处理;
|
||||
|
||||
|
||||
> “反应”器名字中”反应“的由来:“反应”即“倒置”,“控制逆转”,具体事件处理程序不调用反应器,而是由反应器分配一个具体事件处理程序,具体事件处理程序对某个指定的事件发生做出反应;这种控制逆转又称为“好莱坞法则”(不要调用我,让我来调用你)
|
||||
|
||||
### 业务流程及时序图
|
||||
|
||||

|
||||
|
||||
1. 应用启动,将关注的事件handle注册到Reactor中;
|
||||
2. 调用Reactor,进入无限事件循环,等待注册的事件到来;
|
||||
3. 事件到来,select返回,Reactor将事件分发到之前注册的回调函数中处理;
|
||||
|
||||
|
||||
## 2 主动器Proactor
|
||||
|
||||
### Proactor模式结构
|
||||
|
||||

|
||||
|
||||
Proactor主动器模式包含如下角色
|
||||
|
||||
* Handle 句柄;用来标识socket连接或是打开文件;
|
||||
* Asynchronous Operation Processor:异步操作处理器;负责执行异步操作,一般由操作系统内核实现;
|
||||
* Asynchronous Operation:异步操作
|
||||
* Completion Event Queue:完成事件队列;异步操作完成的结果放到队列中等待后续使用
|
||||
* Proactor:主动器;为应用程序进程提供事件循环;从完成事件队列中取出异步操作的结果,分发调用相应的后续处理逻辑;
|
||||
* Completion Handler:完成事件接口;一般是由回调函数组成的接口;
|
||||
* Concrete Completion Handler:完成事件处理逻辑;实现接口定义特定的应用处理逻辑;
|
||||
|
||||
### 业务流程及时序图
|
||||
|
||||

|
||||
|
||||
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:异步接收和同时处理多个服务请求的事件驱动程序;
|
||||
BIN
操作系统/image/2021-04-15-18-37-05.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
操作系统/image/2021-04-15-19-09-12.png
Normal file
|
After Width: | Height: | Size: 152 KiB |
BIN
操作系统/image/2021-04-15-19-09-21.png
Normal file
|
After Width: | Height: | Size: 97 KiB |
BIN
操作系统/image/2021-04-15-19-09-31.png
Normal file
|
After Width: | Height: | Size: 207 KiB |
BIN
操作系统/image/2021-04-15-19-09-40.png
Normal file
|
After Width: | Height: | Size: 143 KiB |
@@ -228,3 +228,8 @@ int pthread_cond_broadcast(pthread_cond_t *cond);
|
||||
* 这两个函数用于通知线程条件已经满足. 调用这两个函数, 也称向线程或条件发送信号. 必须注意, 一定要在改变条件状态以后再给线程发送信号.
|
||||
|
||||
|
||||
## 5 管程
|
||||
|
||||
|
||||
## 6 协程
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
|
||||
# 中断
|
||||
|
||||
|
||||
## 1 中断定义
|
||||
|
||||
### 广义和狭义
|
||||
|
||||
@@ -77,8 +77,8 @@
|
||||
|
||||
### 实现并行编程的几种方法
|
||||
|
||||
1. java 多线程
|
||||
2. nodejs 异步回调
|
||||
3. linux IO复用。epoll事件回调
|
||||
1. java 多线程。
|
||||
2. nodejs 单线程异步回调。非阻塞异步回调。Proactor模式
|
||||
3. linux 单线程IO复用。select/poll/epoll事件回调。多路堵塞,同步回调。reactor模式
|
||||
|
||||
> 这里所谓的什么机制、什么方法。都是设计模式的一部分。通过某种设计模式,实现并行编程:异步回调模式、事件回调模式等。
|
||||
635
设计模式/A 设计模式概述-创建型.md
Normal 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
|
||||
|
||||
使用一个私有构造函数、一个私有静态变量以及一个公有静态函数来实现。
|
||||
|
||||
私有构造函数保证了不能通过构造函数来创建对象实例,只能通过公有静态函数返回唯一的私有静态变量。
|
||||
|
||||

|
||||
|
||||
### 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
|
||||
|
||||
简单工厂把实例化的操作单独放到一个类中,这个类就成为简单工厂类,让简单工厂类来决定应该用哪个具体子类来实例化。
|
||||
|
||||
这样做能把客户类和具体子类的实现解耦,客户类不再需要知道有哪些子类以及应当实例化哪个子类。客户类往往有多个,如果不使用简单工厂,那么所有的客户类都要知道所有子类的细节。而且一旦子类发生改变,例如增加子类,那么所有的客户类都要进行修改。
|
||||
|
||||

|
||||
|
||||
|
||||
### 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() 方法创建。该方法是抽象的,需要由子类去实现。
|
||||
|
||||

|
||||
|
||||
### 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,而工厂方法模式使用了继承。
|
||||
|
||||
|
||||

|
||||
|
||||
### 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
|
||||
|
||||
|
||||

|
||||
|
||||
### 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
|
||||
|
||||

|
||||
|
||||
### 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)
|
||||
777
设计模式/B 设计模式概述-结构型.md
Normal 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
|
||||
|
||||
把一个类接口转换成另一个用户需要的接口。
|
||||
|
||||

|
||||
|
||||
|
||||
### Class Diagram
|
||||
|
||||

|
||||
|
||||
### 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:定义实现类接口
|
||||
|
||||
|
||||

|
||||
|
||||
### 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)的父类,可以把组合类看成是树的中间节点。
|
||||
|
||||
组合对象拥有一个或者多个组件对象,因此组合对象的操作可以委托给组件对象去处理,而组件对象可以是另一个组合对象或者叶子对象。
|
||||
|
||||
|
||||

|
||||
|
||||
### 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),具体组件的方法实现不需要依赖于其它对象,而装饰者组合了一个组件,这样它可以装饰其它装饰者或者具体组件。所谓装饰,就是把这个装饰者套在被装饰者之上,从而动态扩展被装饰者的功能。装饰者的方法有一部分是自己的,这属于它的功能,然后调用被装饰者的方法实现,从而也保留了被装饰者的功能。可以看到,具体组件应当是装饰层次的最低层,因为只有具体组件的方法实现不需要依赖于其它对象。
|
||||
|
||||

|
||||
|
||||
### Implementation
|
||||
|
||||
设计不同种类的饮料,饮料可以添加配料,比如可以添加牛奶,并且支持动态添加新配料。每增加一种配料,该饮料的价格就会增加,要求计算一种饮料的价格。
|
||||
|
||||
下图表示在 DarkRoast 饮料上新增新添加 Mocha 配料,之后又添加了 Whip 配料。DarkRoast 被 Mocha 包裹,Mocha 又被 Whip 包裹。它们都继承自相同父类,都有 cost() 方法,外层类的 cost() 方法调用了内层类的 cost() 方法。
|
||||
|
||||
|
||||

|
||||
|
||||
```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
|
||||
|
||||

|
||||
|
||||
### 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:外部状态,每个享元对象的外部状态不同
|
||||
|
||||

|
||||
|
||||
### 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):取代了简单的指针,它在访问对象时执行一些附加操作:记录对象的引用次数;当第一次引用一个对象时,将它装入内存;在访问一个实际对象前,检查是否已经锁定了它,以确保其它对象不能改变它。
|
||||
|
||||
|
||||
|
||||

|
||||
|
||||
|
||||
### 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)
|
||||
83
设计模式/D IO设计模式.md
Normal file
@@ -0,0 +1,83 @@
|
||||
|
||||
|
||||
## Rector设计模式
|
||||
|
||||
### reactor模式结构
|
||||

|
||||
|
||||
Reactor包含如下角色:
|
||||
|
||||
* Handle 句柄;用来标识socket连接或是打开文件;
|
||||
* Synchronous Event Demultiplexer:同步事件多路分解器:由操作系统内核实现的一个函数;用于阻塞等待发生在句柄集合上的一个或多个事件;(如select/epoll;)
|
||||
* Event Handler:事件处理接口
|
||||
* Concrete Event HandlerA:实现应用程序所提供的特定事件处理逻辑;
|
||||
* Reactor:反应器,定义一个接口,实现以下功能:
|
||||
1. 供应用程序注册和删除关注的事件句柄;
|
||||
2. 运行事件循环;
|
||||
3. 有就绪事件到来时,分发事件到之前注册的回调函数上处理;
|
||||
|
||||
|
||||
> “反应”器名字中”反应“的由来:“反应”即“倒置”,“控制逆转”,具体事件处理程序不调用反应器,而是由反应器分配一个具体事件处理程序,具体事件处理程序对某个指定的事件发生做出反应;这种控制逆转又称为“好莱坞法则”(不要调用我,让我来调用你)
|
||||
|
||||
### 业务流程及时序图
|
||||
|
||||

|
||||
|
||||
1. 应用启动,将关注的事件handle注册到Reactor中;
|
||||
2. 调用Reactor,进入无限事件循环,等待注册的事件到来;
|
||||
3. 事件到来,select返回,Reactor将事件分发到之前注册的回调函数中处理;
|
||||
|
||||
|
||||
## 2 主动器Proactor
|
||||
|
||||
### Proactor模式结构
|
||||
|
||||

|
||||
|
||||
Proactor主动器模式包含如下角色
|
||||
|
||||
* Handle 句柄;用来标识socket连接或是打开文件;
|
||||
* Asynchronous Operation Processor:异步操作处理器;负责执行异步操作,一般由操作系统内核实现;
|
||||
* Asynchronous Operation:异步操作
|
||||
* Completion Event Queue:完成事件队列;异步操作完成的结果放到队列中等待后续使用
|
||||
* Proactor:主动器;为应用程序进程提供事件循环;从完成事件队列中取出异步操作的结果,分发调用相应的后续处理逻辑;
|
||||
* Completion Handler:完成事件接口;一般是由回调函数组成的接口;
|
||||
* Concrete Completion Handler:完成事件处理逻辑;实现接口定义特定的应用处理逻辑;
|
||||
|
||||
### 业务流程及时序图
|
||||

|
||||
|
||||
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:异步接收和同时处理多个服务请求的事件驱动程序;
|
||||
BIN
设计模式/image/2021-04-15-18-54-18.png
Normal file
|
After Width: | Height: | Size: 152 KiB |
BIN
设计模式/image/2021-04-15-18-59-23.png
Normal file
|
After Width: | Height: | Size: 97 KiB |
BIN
设计模式/image/2021-04-15-19-00-27.png
Normal file
|
After Width: | Height: | Size: 207 KiB |
BIN
设计模式/image/2021-04-15-19-04-13.png
Normal file
|
After Width: | Height: | Size: 143 KiB |