行为型模式:描述多个类或对象怎样交互以及怎样分配职责
策略模式
策略模式定义:就是能够把一系列“可互换的”算法封装起来,并根据用户需求来选择其中一种。
什么是策略模式?
策略模式定义:就是能够把一系列“可互换的”算法封装起来,并根据用户需求来选择其中一种。
策略模式的实现核心就是:将算法的使用和算法的实现分离。算法的实现交给策略类。算法的使用交给环境类,环境类会根据不同的情况选择合适的算法。
策略模式优缺点
在使用策略模式的时候,需要了解所有的“策略”(strategy)之间的异同点,才能选择合适的“策略”进行调用。
代码实现
python3 实现
| 12
 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
 
 | class Stragegy():
 def interface(self):
 raise NotImplementedError()
 
 class StragegyA():
 def interface(self):
 print("This is stragegy A")
 
 class StragegyB():
 def interface(self):
 print("This is stragegy B")
 
 class Context():
 def __init__(self, stragegy):
 self.__stragegy = stragegy()
 
 def update_stragegy(self, stragegy):
 self.__stragegy = stragegy()
 
 def interface(self):
 return self.__stragegy.interface()
 if __name__ == "__main__":
 
 cxt = Context( StragegyA )
 cxt.interface()
 
 cxt.update_stragegy( StragegyB )
 cxt.interface()
 
 
 | 
javascript 实现
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 
 | // 策略类const strategies = {
 A() {
 console.log("This is stragegy A");
 },
 B() {
 console.log("This is stragegy B");
 }
 };
 // 环境类
 const context = name => {
 return strategies[name]();
 };
 // 调用策略A
 context("A");
 // 调用策略B
 context("B");
 
 
 | 
迭代器模式
迭代器模式是指提供一种方法顺序访问一个集合对象的各个元素,使用者不需要了解集合对象的底层实现。
什么是迭代器模式?
迭代器模式是指提供一种方法顺序访问一个集合对象的各个元素,使用者不需要了解集合对象的底层实现。
内部迭代器和外部迭代器
内部迭代器:封装的方法完全接手迭代过程,外部只需要一次调用。
外部迭代器:用户必须显式地请求迭代下一元素。熟悉 C++的朋友,可以类比 C++内置对象的迭代器的 end()、next()等方法。
python3 实现
python3 的迭代器可以用作for()循环和next()方法的对象。同时,在实现迭代器的时候,可以在借助生成器yield。python 会生成传给yeild的值。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 
 | def my_iter():yield 0, "first"
 yield 1, "second"
 yield 2, "third"
 if __name__ == "__main__":
 
 for (index, item) in my_iter():
 print("At", index , "is", item)
 
 
 _iter = iter(my_iter())
 while True:
 try:
 index,item = next(_iter)
 print("At", index , "is", item)
 except StopIteration:
 break
 
 
 | 
ES6 实现
这里实现的是一个外部迭代器。需要实现边界判断函数、元素获取函数和更新索引函数。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 
 | const Iterator = obj => {let current = 0;
 let next = () => (current += 1);
 let end = () => current >= obj.length;
 let get = () => obj[current];
 return {
 next,
 end,
 get
 };
 };
 let myIter = Iterator([1, 2, 3]);
 while (!myIter.end()) {
 console.log(myIter.get());
 myIter.next();
 }
 
 
 | 
订阅发布模式
订阅-发布模式:定义了对象之间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖它的对象都可以得到通知。
什么是“订阅-发布模式”?
订阅-发布模式:定义了对象之间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖它的对象都可以得到通知。
了解过事件机制或者函数式编程的朋友,应该会体会到“订阅-发布模式”所带来的“时间解耦”和“空间解耦”的优点。借助函数式编程中闭包和回调的概念,可以很优雅地实现这种设计模式。
“订阅-发布模式” vs 观察者模式
订阅-发布模式和观察者模式概念相似,但在订阅-发布模式中,订阅者和发布者之间多了一层中间件:一个被抽象出来的信息调度中心。
但其实没有必要太深究 2 者区别,因为《Head First 设计模式》这本经典书都写了:发布+订阅=观察者模式。其核心思想是状态改变和发布通知。在此基础上,根据语言特性,进行实现即可。
python3 实现
python 中我们定义一个事件类Event, 并且为它提供 事件监听函数、(事件完成后)触发函数,以及事件移除函数。任何类都可以通过继承这个通用事件类,来实现“订阅-发布”功能。
| 12
 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
 
 | class Event:def __init__(self):
 self.client_list = {}
 def listen(self, key, fn):
 if key not in self.client_list:
 self.client_list[key] = []
 self.client_list[key].append(fn)
 def trigger(self, *args, **kwargs):
 fns = self.client_list[args[0]]
 length = len(fns)
 if not fns or length == 0:
 return False
 for fn in fns:
 fn(*args[1:], **kwargs)
 return False
 def remove(self, key, fn):
 if key not in self.client_list or not fn:
 return False
 fns = self.client_list[key]
 length = len(fns)
 for _fn in fns:
 if _fn == fn:
 fns.remove(_fn)
 return True
 
 class SalesOffice(Event):
 def __init__(self):
 super().__init__()
 
 def handle_event(event_name):
 def _handle_event(*args, **kwargs):
 print("Price is", *args, "at", event_name)
 return _handle_event
 if __name__ == "__main__":
 
 fn1 = handle_event("event01")
 fn2 = handle_event("event02")
 sales_office = SalesOffice()
 
 sales_office.listen("event01", fn1)
 sales_office.listen("event02", fn2)
 
 sales_office.trigger("event01", 1000)
 sales_office.trigger("event02", 2000)
 sales_office.remove("event01", fn1)
 
 print(sales_office.trigger("event01", 1000))
 
 | 
ES6 实现
JS 中一般用事件模型来代替传统的发布-订阅模式。任何一个对象的原型链被指向Event的时候,这个对象便可以绑定自定义事件和对应的回调函数。
| 12
 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
 
 | const Event = {clientList: {},
 
 listen(key, fn) {
 if (!this.clientList[key]) {
 this.clientList[key] = [];
 }
 this.clientList[key].push(fn);
 return true;
 },
 
 trigger() {
 const key = Array.prototype.shift.apply(arguments),
 fns = this.clientList[key];
 if (!fns || fns.length === 0) {
 return false;
 }
 for (let fn of fns) {
 fn.apply(null, arguments);
 }
 return true;
 },
 
 remove(key, fn) {
 let fns = this.clientList[key];
 
 
 
 if (!fns || !fn) {
 return false;
 }
 
 for (let l = fns.length - 1; l >= 0; l--) {
 let _fn = fns[l];
 if (_fn === fn) {
 fns.splice(l, 1);
 }
 }
 return true;
 }
 };
 
 const installEvent = obj => {
 for (let key in Event) {
 obj[key] = Event[key];
 }
 };
 let salesOffices = {};
 installEvent(salesOffices);
 
 salesOffices.listen(
 "event01",
 (fn1 = price => {
 console.log("Price is", price, "at event01");
 })
 );
 salesOffices.listen(
 "event02",
 (fn2 = price => {
 console.log("Price is", price, "at event02");
 })
 );
 salesOffices.trigger("event01", 1000);
 salesOffices.trigger("event02", 2000);
 salesOffices.remove("event01", fn1);
 
 
 console.log(salesOffices.trigger("event01", 1000));
 
 | 
命令模式
命令模式定义:将一个请求封装为一个对象,从而使我们可用不同的请求对客户进行参数化;对请求排队或者记录请求日志,以及支持可撤销的操作。
什么是“命令模式”?
命令模式(别名:动作模式、事务模式)定义:将一个请求封装为一个对象,从而使我们可用不同的请求对客户进行参数化;对请求排队或者记录请求日志,以及支持可撤销的操作。
简单来说,它的核心思想是:不直接调用类的内部方法,而是通过给“指令函数”传递参数,由“指令函数”来调用类的内部方法。
在这过程中,分别有 3 个不同的主体:调用者、传递者和执行者。
应用场景
当想降低调用者与执行者(类的内部方法)之间的耦合度时,可以使用此种设计模式。比如:设计一个命令队列,将命令调用记入日志。
ES6 实现
为了方便演示,这里模拟了购物的场景。封装一个商场类,可以查看已有商品的名称和单价。
| 12
 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
 
 | const mockData = {
 10001: {
 name: "电视",
 price: 3888
 },
 10002: {
 name: "MacPro",
 price: 17000
 }
 };
 
 
 
 class Mall {
 static request(id) {
 if (!mockData[id]) {
 return `商品不存在`;
 }
 const { name, price } = mockData[id];
 return `商品名: ${name} 单价: ${price}`;
 }
 static buy(id, number) {
 if (!mockData[id]) {
 return `商品不存在`;
 }
 if (number < 1) {
 return `至少购买1个商品`;
 }
 return mockData[id].price * number;
 }
 }
 
 | 
毫无疑问,我们可以直接调用商场类上的方法。但是这样会增加调用者和执行者的耦合度。如果之后的函数名称改变了,那么修改成本自然高。
根据命令模式的思想,封装一个“传递者”函数,专门用来传递指令和参数。如果之后商场类的函数名改变了,只需要在“传递者”函数中做个简单映射即可。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 
 | 
 
 function execCmd(cmd, ...args) {
 if (typeof Mall[cmd] !== "function") {
 return;
 }
 console.log(`<LOG> At ${Date.now()}, call ${cmd}`);
 return Mall[cmd](...args);
 }
 
 | 
最后,下面代码展示了外界的“调用者”如何调用命令:
| 12
 3
 
 | console.log(execCmd("request", 10001));
 console.log("10个mbp的总价是", execCmd("buy", 10002, 10));
 
 | 
更多思考
在写这篇文章的时候,发现“命令模式”的思路,可以很好的组织不同版本的 api 调用。只需要在“传递者”函数中进行版本识别,然后传递到对应版本的类中即可。
这对于外界调用者来说,是无感的。即便想调用老版本的函数 api,也可以通过给“传递者”函数指定代表版本的参数来实现。
责任链模式
责任链模式定义:多个对象均有机会处理请求,从而解除发送者和接受者之间的耦合关系。这些对象连接成为“链式结构”,每个节点转发请求,直到有对象处理请求为止。
其核心思想就是:请求者不必知道是谁哪个节点对象处理的请求。如果当前不符合终止条件,那么把请求转发给下一个节点处理。
什么是“责任链模式”?
责任链模式定义:多个对象均有机会处理请求,从而解除发送者和接受者之间的耦合关系。这些对象连接成为“链式结构”,每个节点转发请求,直到有对象处理请求为止。
其核心思想就是:请求者不必知道是谁哪个节点对象处理的请求。如果当前不符合终止条件,那么把请求转发给下一个节点处理。
而当需求具有“传递”的性质时(代码中其中一种体现就是:多个if、else if、else if、else嵌套),就可以考虑将每个分支拆分成一个节点对象,拼接成为责任链。
优点与代价
优点:
- 可以根据需求变动,任意向责任链中添加 / 删除节点对象
- 没有固定的“开始节点”,可以从任意节点开始
代价:责任链最大的代价就是每个节点带来的多余消耗。当责任链过长,很多节点只有传递的作用,而不是真正地处理逻辑。
代码实现
为了方便演示,模拟常见的“日志打印”场景。模拟了 3 种级别的日志输出:
- LogHandler: 普通日志
- WarnHandler:警告日志
- ErrorHandler:错误日志
首先我们会构造“责任链”:LogHandler -> WarnHandler -> ErrorHandler。LogHandler作为链的开始节点。
如果是普通日志,那么就由 LogHandler 处理,停止传播;如果是 Warn 级别的日志,那么 LogHandler 就会自动向下传递,WarnHandler 接收到并且处理,停止传播;Error 级别日志同理。
ES6 实现
| 12
 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
 
 | class Handler {constructor() {
 this.next = null;
 }
 setNext(handler) {
 this.next = handler;
 }
 }
 class LogHandler extends Handler {
 constructor(...props) {
 super(...props);
 this.name = "log";
 }
 handle(level, msg) {
 if (level === this.name) {
 console.log(`LOG: ${msg}`);
 return;
 }
 this.next && this.next.handle(...arguments);
 }
 }
 class WarnHandler extends Handler {
 constructor(...props) {
 super(...props);
 this.name = "warn";
 }
 handle(level, msg) {
 if (level === this.name) {
 console.log(`WARN: ${msg}`);
 return;
 }
 this.next && this.next.handle(...arguments);
 }
 }
 class ErrorHandler extends Handler {
 constructor(...props) {
 super(...props);
 this.name = "error";
 }
 handle(level, msg) {
 if (level === this.name) {
 console.log(`ERROR: ${msg}`);
 return;
 }
 this.next && this.next.handle(...arguments);
 }
 }
 
 let logHandler = new LogHandler();
 let warnHandler = new WarnHandler();
 let errorHandler = new ErrorHandler();
 
 logHandler.setNext(warnHandler);
 warnHandler.setNext(errorHandler);
 logHandler.handle("error", "Some error occur");
 
 | 
Python3 实现
| 12
 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
 
 | class Handler():def __init__(self):
 self.next = None
 def set_next(self, handler):
 self.next = handler
 class LogHandler(Handler):
 def __init__(self):
 super().__init__()
 self.__name = "log"
 def handle(self, level, msg):
 if level == self.__name:
 print('LOG: ', msg)
 return
 if self.next != None:
 self.next.handle(level, msg)
 class WarnHandler(Handler):
 def __init__(self):
 super().__init__()
 self.__name = "warn"
 def handle(self, level, msg):
 if level == self.__name:
 print('WARN: ', msg)
 return
 if self.next != None:
 self.next.handle(level, msg)
 class ErrorHandler(Handler):
 def __init__(self):
 super().__init__()
 self.__name = "error"
 def handle(self, level, msg):
 if level == self.__name:
 print('ERROR: ', msg)
 return
 if self.next != None:
 self.next.handle(level, msg)
 
 log_handler = LogHandler()
 warn_handler = WarnHandler()
 error_handler = ErrorHandler()
 
 log_handler.set_next(warn_handler)
 warn_handler.set_next(error_handler)
 log_handler.handle("error", "Some error occur")
 
 | 
状态模式
状态模式:对象行为是根据状态改变,而改变的。
什么是“状态模式”?
状态模式:对象行为是根据状态改变,而改变的。
正是由于内部状态的变化,导致对外的行为发生了变化。例如:相同的方法在不同时刻被调用,行为可能会有差异。
优缺点
优点:
- 封装了转化规则,对于大量分支语句,可以考虑使用状态类进一步封装。
- 每个状态都是确定的,对象行为是可控的。
缺点:状态模式的实现关键是将事物的状态都封装成单独的类,这个类的各种方法就是“此种状态对应的表现行为”。因此,程序开销会增大。
ES6 实现
在 JavaScript 中,可以直接用 JSON 对象来代替状态类。
下面代码展示的就是 FSM(有限状态机)里面有 3 种状态:download、pause、deleted。控制状态转化的代码也在其中。
DownLoad类就是,常说的Context对象,它的行为会随着状态的改变而改变。
| 12
 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
 
 | const FSM = (() => {let currenState = "download";
 return {
 download: {
 click: () => {
 console.log("暂停下载");
 currenState = "pause";
 },
 del: () => {
 console.log("先暂停, 再删除");
 }
 },
 pause: {
 click: () => {
 console.log("继续下载");
 currenState = "download";
 },
 del: () => {
 console.log("删除任务");
 currenState = "deleted";
 }
 },
 deleted: {
 click: () => {
 console.log("任务已删除, 请重新开始");
 },
 del: () => {
 console.log("任务已删除");
 }
 },
 getState: () => currenState
 };
 })();
 class Download {
 constructor(fsm) {
 this.fsm = fsm;
 }
 handleClick() {
 const { fsm } = this;
 fsm[fsm.getState()].click();
 }
 hanldeDel() {
 const { fsm } = this;
 fsm[fsm.getState()].del();
 }
 }
 
 let download = new Download(FSM);
 download.handleClick();
 download.handleClick();
 download.hanldeDel();
 download.handleClick();
 download.hanldeDel();
 
 | 
Python3 实现
python 的代码采用的是“面向对象”的编程,没有过度使用函数式的闭包写法(python 写起来也不难)。
因此,负责状态转化的类,专门拿出来单独封装。
其他 3 个状态类的状态,均由这个状态类来管理。
| 12
 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
 
 | class StateTransform:
 def __init__(self):
 self.__state = 'download'
 self.__states = ['download', 'pause', 'deleted']
 def change(self, to_state):
 if (not to_state) or (to_state not in self.__states) :
 raise Exception('state is unvalid')
 self.__state = to_state
 def get_state(self):
 return self.__state
 
 class DownloadState:
 def __init__(self, transfomer):
 self.__state = 'download'
 self.__transfomer = transfomer
 def click(self):
 print('暂停下载')
 self.__transfomer.change('pause')
 def delete(self):
 print('先暂停, 再删除')
 class PauseState:
 def __init__(self, transfomer):
 self.__state = 'pause'
 self.__transfomer = transfomer
 def click(self):
 print('继续下载')
 self.__transfomer.change('download')
 def delete(self):
 print('删除任务')
 self.__transfomer.change('deleted')
 class DeletedState:
 def __init__(self, transfomer):
 self.__state = 'deleted'
 self.__transfomer = transfomer
 def click(self):
 print('任务已删除, 请重新开始')
 def delete(self):
 print('任务已删除')
 
 class Download:
 def __init__(self):
 self.state_transformer = StateTransform()
 self.state_map = {
 'download': DownloadState(self.state_transformer),
 'pause': PauseState(self.state_transformer),
 'deleted': DeletedState(self.state_transformer)
 }
 def handle_click(self):
 state = self.state_transformer.get_state()
 self.state_map[state].click()
 def handle_del(self):
 state = self.state_transformer.get_state()
 self.state_map[state].delete()
 if __name__ == '__main__':
 download = Download()
 download.handle_click();
 download.handle_click();
 download.handle_del();
 download.handle_click();
 download.handle_del();
 
 | 
解释器模式
解释器模式: 提供了评估语言的语法或表达式的方式。
什么是“解释器模式?
解释器模式定义: 提供了评估语言的语法或表达式的方式。
这是基本不怎么使用的一种设计模式。确实想不到什么场景一定要用此种设计模式。
实现这种模式的核心是:
- 抽象表达式:主要有一个interpret()操作
- 终结符表达式:R = R1 + R2中,R1R2就是终结符
- 非终结符表达式:R = R1 - R2中,就是终结符
- 环境(Context): 存放文法中各个终结符所对应的具体值。比如前面R1和R2的值。
优缺点
优点显而易见,每个文法规则可以表述为一个类或者方法。这些文法互相不干扰,符合“开闭原则”。
由于每条文法都需要构建一个类或者方法,文法数量上去后,很难维护。并且,语句的执行效率低(一直在不停地互相调用)。
ES6 实现
为了方便说明,下面省略了“抽象表达式”的实现。
| 12
 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
 
 | class Context {constructor() {
 this._list = [];
 this._sum = 0;
 }
 get sum() {
 return this._sum;
 }
 set sum(newValue) {
 this._sum = newValue;
 }
 add(expression) {
 this._list.push(expression);
 }
 get list() {
 return [...this._list];
 }
 }
 class PlusExpression {
 interpret(context) {
 if (!(context instanceof Context)) {
 throw new Error("TypeError");
 }
 context.sum = ++context.sum;
 }
 }
 class MinusExpression {
 interpret(context) {
 if (!(context instanceof Context)) {
 throw new Error("TypeError");
 }
 context.sum = --context.sum;
 }
 }
 
 const context = new Context();
 
 context.add(new PlusExpression());
 context.add(new PlusExpression());
 context.add(new MinusExpression());
 
 context.list.forEach(expression => expression.interpret(context));
 console.log(context.sum);
 
 | 
备忘录模式
备忘录模式:属于行为模式,保存某个状态,并且在需要的时候直接获取,而不是重复计算。
什么是备忘录模式
备忘录模式:属于行为模式,保存某个状态,并且在需要的时候直接获取,而不是重复计算。
注意:备忘录模式实现,不能破坏原始封装。也就是说,能拿到内部状态,将其保存在外部。
应用场景
最典型的例子是“斐波那契数列”递归实现。
不借助备忘录模式,数据一大,就容易爆栈;借助备忘录,算法的时间复杂度可以降低到$ O(N) $
除此之外,数据的缓存等也是常见应用场景。
ES6 实现
首先模拟了一下简单的拉取分页数据。
如果当前数据没有被缓存,那么就模拟异步请求,并将结果放入缓存中;
如果已经缓存过,那么立即取出即可,无需多次请求。
main.js:
| 12
 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
 
 | const fetchData = (() => {
 const cache = {};
 return page =>
 new Promise(resolve => {
 
 if (page in cache) {
 return resolve(cache[page]);
 }
 
 
 setTimeout(() => {
 cache[page] = `内容是${page}`;
 resolve(cache[page]);
 }, 1000);
 });
 })();
 
 const run = async () => {
 let start = new Date().getTime(),
 now;
 
 await fetchData(1);
 now = new Date().getTime();
 console.log(`没有缓存, 耗时${now - start}ms`);
 
 start = now;
 await fetchData(1);
 now = new Date().getTime();
 console.log(`有缓存, 耗时${now - start}ms`);
 };
 run();
 
 | 
最近在项目中还遇到一个场景,在React中加载微信登陆二维码。
这需要编写一个插入script标签的函数。
要考虑的情况是:
- 同一个script标签不能被多次加载
- 对于加载错误,要正确处理
- 对于几乎同时触发加载函数的情况, 应该考虑锁住
基于此,main2.js文件编码如下:
| 12
 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
 
 | const loadScript = src => {
 let exists = false;
 return () =>
 new Promise((resolve, reject) => {
 if (exists) return resolve();
 
 exists = true;
 
 let script = document.createElement("script");
 script.src = src;
 script.type = "text/javascript";
 script.onerror = ev => {
 
 script.remove();
 exists = false;
 reject(new Error("Load Error"));
 };
 script.onload = () => {
 
 resolve();
 };
 document.body.appendChild(script);
 });
 };
 
 
 const wxLoader = loadScript(
 "<https://res.wx.qq.com/connect/zh_CN/htmledition/js/wxLogin.jser>"
 );
 
 setInterval(() => {
 wxLoader()
 .then()
 .catch(error => console.log(error.message));
 }, 5000);
 
 | 
在index2.html中引入上述代码,即可查看效果:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 
 | <!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>Document</title>
 </head>
 <body>
 <script src="./main2.js"></script>
 </body>
 </html>
 
 | 
python3 实现
这里实现一下借助“备忘录模式”优化过的、递归写法的“斐波那契数列”。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 
 | def fibonacci(n):
 mem = {1: 1, 2: 1}
 def _fibonacci(_n):
 
 if _n in mem:
 return mem[_n]
 mem[_n] = _fibonacci(_n - 1) + _fibonacci(_n - 2)
 return mem[_n]
 return _fibonacci(n)
 if __name__ == '__main__':
 print(fibonacci(999))
 
 | 
模版模式
模板模式是:抽象父类定义了子类需要重写的相关方法。并且这些方法,仍然是通过父类方法调用的。
什么是模板模式?
模板模式是:抽象父类定义了子类需要重写的相关方法。并且这些方法,仍然是通过父类方法调用的。
根据描述,父类提供了“模板”并决定是否调用,子类进行具体实现。
应用场景
一些系统的架构或者算法骨架,由“BOSS”编写抽象方法,具体的实现,交给“小弟们”实现。
而用不用“小弟们”的方法,还是看“BOSS”的心情。
ES6 实现
Animal是抽象类,Dog和Cat分别具体实现了eat()和sleep()方法。
Dog或Cat实例可以通过live()方法调用eat()和sleep()。
注意:Cat和Dog实例会被自动添加live()方法。不暴露live()是为了防止live()被子类重写,保证父类的控制权。
| 12
 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
 
 | class Animal {constructor() {
 
 this.live = () => {
 this.eat();
 this.sleep();
 };
 }
 eat() {
 throw new Error("模板类方法必须被重写");
 }
 sleep() {
 throw new Error("模板类方法必须被重写");
 }
 }
 class Dog extends Animal {
 constructor(...args) {
 super(...args);
 }
 eat() {
 console.log("狗吃粮");
 }
 sleep() {
 console.log("狗睡觉");
 }
 }
 class Cat extends Animal {
 constructor(...args) {
 super(...args);
 }
 eat() {
 console.log("猫吃粮");
 }
 sleep() {
 console.log("猫睡觉");
 }
 }
 
 
 let dog = new Dog();
 dog.live();
 
 let cat = new Cat();
 cat.live();
 
 | 
参考