Skip to content

Commit e7190fa

Browse files
committed
⚡ feat: 使用vuepress构建在线阅读网站
1 parent cf5baba commit e7190fa

21 files changed

+11977
-0
lines changed

.DS_Store

6 KB
Binary file not shown.

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
node_modules
2+
dp

README.md

+2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
# JavaScript-DesignPatterns
22
JavaScript设计模式的相关学习
33

4+
在线阅读地址: [JavaScript-DesignPatterns](https://reaperlee.cn/dp/)
5+
46
## 目录
57
### 创建型
68
- [工厂模式](https://github.com/Reaper622/JavaScript-DesignPatterns/blob/master/Factory/Factory.md)

docs/.vuepress/config.js

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
module.exports = {
2+
title: 'JavaScript设计模式',
3+
dest: './dp',
4+
base: '/dp/',
5+
repo: 'https://github.com/Reaper622/JavaScript-DesignPatterns',
6+
themeConfig: {
7+
// 添加导航栏
8+
nav: [
9+
{text: '设计模式', link: '/'},
10+
{text: '数据结构和算法', link: 'https://reaperlee.cn/ds-al'},
11+
{text: '博客', link: 'https://reaperlee.cn'},
12+
]
13+
}
14+
}

docs/Adapter.md

+74
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# 适配器模式
2+
3+
> 适配器模式主要用来解决两个已有接口之间不匹配的问题,它不考虑这些接口是怎么实现的,也不考虑他们将来会如何演化。适配器模式不需要改变当前已有的接口,就能让他们协同作用。
4+
5+
适配器的别名也叫包装器(wrapper),这是一个并不复杂的模式,在日常开发中有许多这样的场景:例如当我们试图调用某个模块或者某个对象的接口时,却发现这个接口的格式并不符合目前的需求,这时就有两种解决方法,第一种使我们直接修改原来的接口实现,但如果原来的模块或者对象很复杂,亦或是我们拿到的已经是已压缩过的代码,那么去修改原接口就显得不现实了。第二种方法就是我们要讲到的适配器,将原接口转换成你希望拿到的接口,而你只需要通过适配器即可得到,并不需要去修改原模块或对象。
6+
7+
举一个抽象的例子,例如当我们有两台电脑需要充电:
8+
9+
```javascript
10+
class ThinkPad {
11+
charge() {
12+
console.log('ThinkPad 开始充电');
13+
}
14+
}
15+
16+
17+
class MacBook {
18+
charge() {
19+
console.log('MacBook 开始充电')
20+
}
21+
}
22+
// 电源充电
23+
function PowerToCharge(laptop) {
24+
if(laptop.charge instanceof Function) {
25+
laptop.charge()
26+
}
27+
}
28+
29+
PowerToCharge(new ThinkPad()) // ThinkPad开始充电
30+
PowerToCharge(new MacBook()) // MacBook开始充电
31+
```
32+
33+
但是如果MacBook不能直接用一种电源接口充电,可能我们就需要一种转接器,这里也就使用的适配器模式,我们不能直接更改电脑上的接口,但我们可以通过一层转接(封装),来实现充电
34+
35+
```javascript
36+
class ThinkPad {
37+
charge() {
38+
console.log('ThinkPad 开始充电');
39+
}
40+
}
41+
42+
43+
class MacBook {
44+
type_cCharge() {
45+
console.log('MacBook 开始充电')
46+
}
47+
}
48+
49+
// 定义适配器类,来实现对MacBook的转接
50+
class TypeCToDp {
51+
charge() {
52+
let macbook = new MacBook();
53+
return macbook.type_cCharge()
54+
}
55+
}
56+
// 电源充电
57+
function PowerToCharge(laptop) {
58+
if(laptop.charge instanceof Function) {
59+
laptop.charge()
60+
}
61+
}
62+
63+
PowerToCharge(new ThinkPad()) // ThinkPad开始充电
64+
PowerToCharge(new TypeCToDp()) // MacBook开始充电
65+
```
66+
67+
68+
69+
#### 总结
70+
71+
- 适配器模式虽然是一种相对简单的模式,但适配器在JS或者BFF层使用的场景很多。
72+
- 但同时,我们要意识到适配器模式其实一种补救措施,它用来解决的是一些古老不可维护或者已经在稳定版本的两个接口不兼容的问题,而在开发初期应该减少或者不使用这种模式,而是要规划好接口的一致性。
73+
- 适配器不会改变原有的接口,而是一个对象对另一个对象的包装。
74+
- 适配器模式符合开放封闭原则。

docs/Appearance.md

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# 外观模式
2+
3+
> 外观模式为一组复杂的子系统接口提供一个更高级的统一接口,通过这个接口使得对子系统接口的访问更容易,不符合单一职责原则和开放封闭原则。
4+
5+
其实外观模式很常见,它其实就是通过一个单独的函数,来简化对一个或多个更大型,更为复杂的函数的访问,是一种对复杂操作的封装。
6+
7+
### 封装Ajax
8+
9+
初始化一个原生 Ajax 请求其实是复杂的,我们可以通过封装来简化
10+
11+
```javascript
12+
function ajaxCall(type, url, callback, data) {
13+
let xhr = (function(){
14+
try {
15+
// 标准方法
16+
return new XMLHttpRequest()
17+
}catch(e){}
18+
19+
try {
20+
return new ActiveXObject("Msxm12.XMLHTTP")
21+
}catch(e){}
22+
}())
23+
STATE_LOADED = 4
24+
STATUS_OK = 200
25+
26+
// 一但从服务器收到表示成功的相应消息,则执行所给定的回调方法
27+
xhr.onreadystatechange = function () {
28+
if (xhr.readyState !== STATE_LOADED) {
29+
return
30+
}
31+
if (xhr.state == STATUS_OK) {
32+
callback(xhr.responseText)
33+
}
34+
}
35+
36+
// 发出请求
37+
xhr.open(type.toUpperCase(), url)
38+
xhr.send(data)
39+
}
40+
```
41+
42+
封装之后,我们发送请求的样子就变成了
43+
44+
```javascript
45+
// 使用封装的方法
46+
ajaxCall("get", "/url/data", function(res) {
47+
document.write(res)
48+
})
49+
```
50+
51+
52+
53+
#### 总结
54+
55+
​ 外观模式适用于当需要同时有多个复杂操作时,通过把复杂操作封装,调用时直接用方法调用,提高代码的可阅读性和可维护性。

docs/Command.md

+136
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
# 命令模式
2+
3+
> 在软件系统里,`行为请求者``行为实现者`通常呈现一种*紧耦合*,但在某些场合,比如要对行为进行`记录、撤销/重做、事务`等处理时,这种无法抵御变化的紧耦合是不合适的。在这种情况下,如果要将`行为请求者``行为实现者`解耦合,我们需要将一组行为抽象为对象,实现二者之间的松耦合,这就是`命令模式`
4+
5+
我们需要在命令的发布者和接受者之间定义一个命令对象,命令对象会暴露一个统一的借口给命令的发布者而命令的发布者不需要去了解接受者是如何执行命令的,做到了命令发布者和接受者的解耦合。
6+
7+
[![u06DRP.png](https://s2.ax1x.com/2019/10/03/u06DRP.png)](https://imgchr.com/i/u06DRP)
8+
9+
我们下面的例子中一个页面有三个按钮,每个按钮有不同的功能:
10+
11+
```html
12+
<!DOCTYPE html>
13+
<html lang="en">
14+
<head>
15+
<meta charset="UTF-8">
16+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
17+
<meta http-equiv="X-UA-Compatible" content="ie=edge">
18+
<title>命令模式</title>
19+
</head>
20+
<body>
21+
<div>
22+
<button id="btn1">Button1</button>
23+
<button id="btn2">Button2</button>
24+
<button id="btn3">Button3</button>
25+
</div>
26+
<script src="./Command.js"></script>
27+
</body>
28+
</html>
29+
```
30+
31+
接下来我们定义一个命令发布者(执行者)的类
32+
33+
```javascript
34+
class Executor {
35+
setCommand(btn, command) {
36+
btn.onclick = function () {
37+
command.execute();
38+
}
39+
}
40+
}
41+
```
42+
43+
接着我们定义一个命令接受者,此例中为一个菜单
44+
45+
```javascript
46+
// 定义一个命令接受者
47+
class Menu {
48+
refresh() {
49+
console.log('刷新菜单')
50+
}
51+
52+
addSubMenu() {
53+
console.log('增加子菜单')
54+
}
55+
56+
deleteMenu() {
57+
console.log('删除菜单')
58+
}
59+
}
60+
```
61+
62+
之后我们将Menu的方法执行封装在单独的类中
63+
64+
```js
65+
66+
class RefreshMenu {
67+
constructor(receiver) {
68+
// 使命令对象与接受者关联
69+
this.receiver = receiver
70+
}
71+
// 暴露出统一接口给 Excetor
72+
execute() {
73+
this.receiver.refresh()
74+
}
75+
}
76+
77+
// 定义一个接受子菜单的命令对象的类
78+
class AddSubMenu {
79+
constructor(receiver) {
80+
// 使命令对象与接受者关联
81+
this.receiver = receiver
82+
}
83+
84+
// 暴露出统一的接口给 Excetor
85+
execute() {
86+
this.receiver.addSubMenu()
87+
}
88+
}
89+
90+
// 定义一个接受删除菜单对象的类
91+
class DeleteMenu {
92+
constructor(receiver) {
93+
this.receiver = receiver
94+
}
95+
96+
// 暴露出统一的接口给 Excetor
97+
execute() {
98+
this.receiver.deleteMenu()
99+
}
100+
}
101+
102+
```
103+
104+
之后我们分别实例化不同的对象
105+
106+
```javascript
107+
// 首先获取按钮对象
108+
let btn1 = document.getElementById('btn1')
109+
let btn2 = document.getElementById('btn2')
110+
let btn3 = document.getElementById('btn3')
111+
112+
let menu = new Menu()
113+
let executor = new Executor()
114+
115+
let refreshMenu = new RefreshMenu(menu)
116+
117+
// 给 按钮1 添加刷新功能
118+
executor.setCommand(btn1, refreshMenu) // 点击按钮1 显示"刷新菜单"
119+
120+
let addSubMenu = new AddSubMenu(menu)
121+
// 给按钮2添加子菜单功能
122+
executor.setCommand(btn2, addSubMenu)// 点击按钮2 显示"添加子菜单"
123+
124+
let deleteMenu = new DeleteMenu(menu)
125+
// 给按钮3添加删除功能
126+
executor.setCommand(btn3, deleteMenu)// 点击按钮3 显示"删除菜单"
127+
128+
```
129+
130+
131+
132+
#### 总结
133+
134+
- 发布者与接受者实现了解耦合,符合单一职责原则。
135+
- 命令可扩展,对请求可以进行排队或日志记录,符合开放—封闭原则。
136+
- 但额外增加了命令对象,存在一定的多余开销。

0 commit comments

Comments
 (0)