-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathfacadeAndAdapter.html
234 lines (203 loc) · 7.38 KB
/
facadeAndAdapter.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>门面模式与适配器模式</title>
<style>
.box {
width: 100px;
height: 100px;
border: 1px solid #f9822f;
}
</style>
</head>
<body>
<div class="box" id="box1"></div>
<div class="box" id="box2"></div>
<div class="box" id="box3"></div>
<script>
window.onload = function () {
/*
门面模式:兼容各个浏览器或组合多个操作。
创建一些方法使得代码更易用,更加容易理解,写起来更轻松,更容易管理,不必在意底层细节。如创建一个方法进行浏览器兼容性的判断,返回能用的 或 创建一个方法组合多个操作
*/
// 案例:一些兼容各个浏览器的方法
let addEvent = function (el, type, fn) {
if (window.addEventListener) {
addEvent = function () {
el.addEventListener(type, fn, false);
}
} else if (window.attachEvent) {
addEvent = function () {
el.attachEvent('on' + type, fn);
}
} else {
addEvent = function () {
el['on' + type] = fn;
}
}
};
// 案例:组合一些函数
function a () {
}
function b () {
}
function ab () {
/** Q:为什么直接在这写a和b的实现代码?
* A:因为创建a和b方法可以获得更多,更精细的粒度控制,更灵活。例如排骨青菜饭,分成炒排骨和炒青菜更灵活。
*/
a();
b();
}
// 案例:js设置html元素样式
function setStyle (ids, prop, val) {
for (let id of ids) {
document.getElementById(id).style[prop] = val;
}
}
// 相当于组合多个setStyle,所以很适用门面模式!
function setCss (ids, styles) {
for (let key of Object.keys(styles)) {
setStyle(ids, key, styles[key]);
}
}
// 使用
setCss(['box1', 'box2', 'box3'], {
width: '130px',
height: '100px',
background: '#f92130'
});
/**
* 门面模式的使用:
* 1 找到合适的代码:例如一些浏览器兼容性检测的,或者重复成组出现的代码。
* 2 对于成组出现的代码划分好粒度
*/
/**
* 利与弊:
* 利:
* 1 提供一个更简化的接口处理一些任务或问题,可以反复使用,让程序员更轻松。
* 2 降低对外部代码的依赖,比如自己创建一个addEvent方法,可以降低对addEventListener或attachEvent的依赖
* 3 避免与下层子系统紧密耦合,(不太懂)
* 弊:
* 容易滥用,影响性能。
*/
/**
* 适配器模式:适配旧的接口
*
*/
// 假如有一个接口原本是传对象类型的参数的,现在改成了传三个字符串,那么可以提供一个适配器。
function interfaceFn(str1, str2, str3) { // 新接口:传3个字符串
}
let clientObj = {
str1: 'str1',
str2: 'str2',
str3: 'str3'
}
function clientToInterfaceAdapter (o) { // 客户端提供一个适配器,兼容原来的传对象的。
interfaceFn(o.str1, o.str2, o.str3);
}
clientToInterfaceAdapter(clientObj);
/**
* 实例:prototype 的 $ 与 YUI 的 get 互转
*/
// $ 接收多个参数
function $() {
var elements = new Array();
for (var i = 0; i < arguments.length; i++) {
var element = arguments[i];
if (typeof element == 'string')
element = document.getElementById(element);
if (arguments.length == 1)
return element;
elements.push(element);
}
return elements;
}
// get 接收字符串或dom数组 或 字符串
YAHOO.util.Dom.get = function (el) {
if (YAHOO.lang.isString(el)) {
return document.getElementById(el);
}
if (YAHOO.lang.isArray(el)) {
var c = [];
for (var i = 0, len = el.length; i < len; ++i) {
c[c.length] = Y.Dom.get(el[i]);
}
return c;
}
if (el) {
return el;
}
return null;
};
// $ 转 get 适配器
function PrototypeToYUIAdapter () {
return YAHOO.util.Dom.get(arguments);
}
$ = PrototypeToYUIAdapter;
// get 转 $ 适配器
function YUIToPrototypeAdapter (el) {
// apply 需要传数组,所以如果el不是数组,如只是字符串的话,需要数组包裹下。
return $.apply(window, el instanceof Array ? el : [el]);
}
YAHOO.util.Dom.get = YUIToPrototypeAdapter;
/**
* 实例:原本使用 fooMail 现在转用 dedMail
*/
// 原本fooMail的getMail只提供一个回调,接收的参数是html片段,使用例子如下。
fooMail.getMail(text => {
document.body.innerHTML = text;
})
DED.util = {
// let o = {msg: 'msg123'} let s = 'msg: {msg}' 将会转为 'msg: msg123'。
substitute (s, o) {
return s.replace(/{([^{}]*)}/g,
function (a, b) {
var r = o[b];
return typeof r === 'string' || typeof r === 'number' ? r : a;
}
);
}
}
// dedMail的getMail参数1是邮件的id,参数2为回调函数,回调中返回字符串文本,使用例子如下。
dedMail.getMail(3, msgObject => {
var resp = eval('(' + msgObject + ')');
var details = '<p><strong>From:</strong> {from}<br>';
details += '<strong>Sent:</strong> {date}</p>';
details += '<p><strong>Message:</strong><br>';
details += '{message}</p>';
document.body.innerHTML = DED.util.substitute(details, resp);
})
// 了解了fooMail和dedMail的使用之后,开始写适配器,其实就是覆写fooMail的getMail,让原本fooMail的getMail调用dedMail的getMail。
let dedMailToFooMailAdapter = {};
dedMailToFooMailAdapter.getMail = function (id, callback) {
dedMail.getMail(id, resp => {
var resp = eval('(' + resp + ')');
var details = '<p><strong>From:</strong> {from}<br>';
details += '<strong>Sent:</strong> {date}</p>';
details += '<p><strong>Message:</strong><br>';
details += '{message}</p>';
// 调用fooMail原本的callback(接收html片段那个)
callback(DED.util.substitute(details, resp));
})
}
fooMail = dedMailToFooMailAdapter;
/**
适配器模式适用场合:
1 用于新的接口与现有的接口不兼容的场合,用来协调语法上的差异,新旧接口应该是要执行类似任务。
2 用户想使用一个更容易的接口,也可使用适配器,比如传三个参数转传对象。
*/
/*
利弊:
利:
避免大规模改写,或更简单使用接口。
弊:
有人认为这是不必要的开销,实际上应该需要重写代码。
新旧接口可能未定型,可能又要改动适配器。
*/
}
</script>
</body>
</html>