【读书】JavaScript 设计模式与开发实践

2016.08.30

《JavaScript 设计模式与开发实践》 曾探 人民邮电出版社 2016年5月第1版

p13

找到变化的部分并封装之,以使得容易替换;而剩下的就是不变的部分。

P49

函数柯里化(currying)的作用是多次收集参数,然后作为数组传给处理函数再一次执行。

其意义在于预处理——将预处理的流程放到一个函数里会更为清晰可控。

P57

惰性加载函数

在函数内部重写引用函数的外部变量的引用,从而在第一次”调用”此变量后,此变量就指向新的正确的函数。

p84

这里的errorMsg不应该包含在strategies的项里。这些项只要返回true/false就好,msg关他们什么事啊!

也许是msg要根据strategie而变化吧。

P86

策略模式的目的是将算法独立出代码的执行部分(content),使计算的代码可复用、实现灵活。

关键点在于看出何为策略的最低依赖描述、考虑如何从content进入算法中。

P89

关于代理模式,所谓代理是在访问者-被访问者这两者间的代理者。被访问者提供了一些接口供任意人访问。这些接口在某些情况下不应被调用或应以另一个形式被调用,这时为了保证接口内的职责单一性,应该隔一层调用这个接口;这一层用于处理那些特定情况,决定如何调用接口,这层就是代理。

代理模式的目的是为了保持接口的职责单一性。

代理模式的主要特点是代理者与被代理者(即被访问者)拥有职责一致的接口,一般接口名称也一致。

由于需要处理的情形很多,代理的模式也各种各样:

保护代理: 用于接口有不同访问权限的情况

虚拟代理: 目的是延迟对接口的访问,到有必要时再访问

缓存代理: 存储接口结果,参数一致时不调用接口直接返回已存结果。

等等。也不是说使用了虚拟代理就不能使用保护代理,以上列表只是说明这些情景下需要代理。

P93

单一职责原则指的是:就一个类(也包括对象与函数)而言,应该仅有一个引起它变化的原因。如果一个类承担了多项职责,就意味着这个类将变得巨大,引起它变化的原因可能会有多个。

职责被定义为”引起变化的原因”。当一个类承担多个职责时,由于其高耦合性,一个职责的处理可能会影响到另一个职责的处理。

代理层能承担部分职责,使接口职责单一。

p124

发布-订阅模式推模型的问题在于:订阅者收到的数据是由发布者决定的。

拉模型的问题在于发布者可能会变成一个公开的对象。

首先,选用拉模型。

1. 发布时传一个对象,此对象上拥有一系列接口供订阅者获取数据。可能需要用代理模式。

2. 在订阅时传入一些函数,这些函数将在结果上调用并返回调用结果(太复杂)。

发布-订阅模式里也用到了策略模式。

P131

命令模式的意义在于将职责分到三部分中:命令者-命令-执行者。另一个更重要的意义是命令将变成可存储、调用的数据。

p132

疑点在于这里的演示,命令moveCommand与执行者animate是绑定的,也就是说moveCommand不能用于命令animate2。如果有多个执行者,必须生成多个对应的命令。

其实可以实现成moveCommand.execute(animate2)。

不过仔细想想假如执行者不止animate2还有xxx2的话?多执行者这种情况可以有的吧?

这样子可能就是实现成:

var MoveCommand = function(receiver, pos){

......

this.receivers = [receiver]

}

MoveCommand.prototype.execute = function(){

if(!arguments.length){

this._execute.apply(this, this.receivers)

} else {

this._execute.apply(this, arguments)

}

}

MoveCommand.prototype._execute = function(receiver){

receiver.start......

}

命令里包含执行者也有好处,存储命令就能存储执行者了。

p132

组合模式的关键是接口一致。

p151

模板方法模式由两部分结构组成,第一部分是抽象父类,第二部分是具体的实现子类。通常在抽象父类中封装了子类的算法框架。,包括实现一些公共方法以及封装子类中所有方法的执行顺序。子类通过这个抽象类,也继承了整个算法结构,并且可以选择重写父类的方法。

p160

钩子方法为什么一定要是方法呢?

因为可以动态得到结果,只是要怎样让外部能改变到钩子是个问题。

  1. 通过this访问。也就是这里的实现。

2. 作为闭包暴露钩子。

p173

假如有2000个文件,uploadDatabase里根本就会存2000个对象。只不过不是2000个upload对象。

所以我认为享元模式的意义在于将数据与模板分离,从而共享了一个结构、一些方法。

需要使用一个对象时,拿数据填模板,就得到指定结构的对象与可用的方法。

而这里所谓的”有多少个内部状态,就有多少个共享对象”,内部状态指的是模板,共享对象指的是模板的相应数据化结果(具体到这里的代码就是flyWeightObj,而不是Upload构造器。Upload构造器说到底跟享元模式无关,只是”元”的抽象类,是内部状态的抽象。12.6.1有例证)。

由于将数据分离,所以在本书的实现中,数据需要一个UploadManager来管理,并为模板里的方法提供一个UploadManager.setExternalState以读取数据填充到指定对象(就是模板本身)。

关于这个实现,我觉得这里flyWeightObj.delFile(id)和uploadManager.setExternalState(id, this)同时出现就不好,相互调用感觉就不好。(应该没有相互引用)

是不是改成flyWeightObj.delFile(id); this.setExternalState(id, flyWeightObj); 会比较好?

貌似他这么做也无不妥,在flyWeightObj.delFile里先将数据填充到本身上再删除数据,是一种合理的做法。毕竟delFile可能会被多次调用,总不能每次调用前都写一句setExternalState吧?

如果不需要存在”元”,那就没有必要使用享元模式。

p185

职责链模式的最大优点就是解耦了请求发送者和N个接受者之间的复杂关系,由于不知道链中的哪个节点可以处理你发出的请求,所以你只需把请求传递给第一个节点即可。

本书实现的职责链的问题在于程序员不知道链的具体模样。所以A-B-D,我要往B后面插个C就必须知道B与D的引用,并修改它们的nextNode属性。

可以var chainCtrl = new ChainCtrl(); var A = chainCtrl.cteateAndAppendNode(fnA), B = ......; var C = chainCtrl.createNode(fnC); chainCtrl.insertAfter(C, B);

以上的具体实现里,不仅要维护一个存在ChainCtrl内部的数组,仍需要实现node.nextNode属性,这样才能够调用B.next()以跳过A从B开始执行职责链。

p187

这里提到AOP(面向切面编程)

AOP解决的问题是希望在函数执行前或执行后插入代码又不希望改动原函数。(如果希望在函数执行中某个位置插入代码,恐怕只能使用钩子)

AOP的关键是用一个函数调用原函数并代替原函数被使用。

顺便一说,LISP里天然实现AOP。

p189

中介者模式解决代码块间联系太多,耦合强的问题。(就像在心脏旁边拆掉一根毛细血管一般,即使一点很小的修改也必须小心翼翼)

p195

这里在构造player时传入teamColor我认为是不对的。”team”是一个外部状态而不是player所固有的东西,应该隔一层在playerFactory这个函数里调用setTeam(player)为player设置属性。要不然以后需求改成”当一个队伍满2人时,新的玩家将进入不满员队伍或新建队伍”,这时候势必要:

  1. 在Player构造器加判断逻辑。这当然不行。
  2. Player构造器不直接加判断逻辑而是使用钩子。我认为这也半斤八两,毕竟要调用与team相关的外部接口。
  3. 使用AOP,替换掉Player。可替换掉构造器貌似不妥。

还是移除在Player里对teamColor属性的设置比较好。这时可以:

1.在playerFactory使用钩子。

2.在playerFactory这个函数里调用setTeam(player)

p195

此处Player.prototype.die 等方法里调用了playerDirector.ReceiveMessage,这样就依赖了中介者模式的playerDirector,日后如果想换个模式势必要改动这里的代码。建议隔一层。

要这么说,所有调用外部接口的都应隔一层了。

p198

这里关于掉线的处理有问题,假如先die再remove,因为remove里没有检查,另一方就不能获得胜利了。

可以考虑使用AOP

p199

说到购买商品的例子,能不能创造一种”流程模式”?将流程放到数组里,调用next()则进入下一个流程,调用jump(fn)则跳到fn对应的流程。

还有就是想说,这里可以使用mvc的理念。在页面上操作,然后进入中介者,然后中介者处理一个数据结构,然后中介者调用一个渲染函数根据这个数据结构对页面进行渲染。

问题在于如何只渲染需要渲染的内容。

p206

这里的changed太过臃肿,耦合度也高,应该把if-else分割成函数放到一个obj里,然后在changed里遍历obj的成员并用职责链模式将它们连接起来。

而剩余部分,或许也可以分割成函数并使用AOP连接。

p208

过度抽象会使得无形状,不直观。

p211

装饰者模式的意图是减少子类与实例的数量。

同名调用同名听起来有点像代理模式。

就逻辑业务上两者的区别,代理者的业务与被代理者的业务是相关的,装饰者与被装饰者的业务可以是毫无关联的。

就实现上而言,代理者是拥有被代理者同名方法的一个对象(或函数);装饰者是在类A的原型方法中调用另一个类B创建的实例的方法,最后调用new A()得到一个实例,或者使用AOP替换原方法。

如果被装饰者是单例,装饰者模式是会改变被装饰者的,代理模式则可以不改变它。

时间: 2024-10-13 18:52:54

【读书】JavaScript 设计模式与开发实践的相关文章

《JavaScript设计模式与开发实践》读书笔记之观察者模式

1.<JavaScript设计模式与开发实践>读书笔记之观察者模式 观察者模式定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知. JavaScript中通常采用事件模型替代传统的观察者模式 1.1 逐步实现观察者模式 以客户看房为例 首先指定谁充当发布者,如售楼处 然后给发布者添加一个缓存列表,用于存放回调函数以便通知订阅者.这里为了让订阅者只接收自己感兴趣的消息,增加一个标识key 最后发布消息时候,发布者遍历缓存列表,依次触发里面存放的订阅者的回

JavaScript 设计模式与开发实践读书笔记 http://www.open-open.com/lib/view/open1469154727495.html

JavaScript 设计模式与开发实践读书笔记 最近利用碎片时间在 Kindle 上面阅读<JavaScript 设计模式与开发实践读书>这本书,刚开始阅读前两章内容,和大家分享下我觉得可以在项目中用的上的一些笔记. 我的 github 项目会不定时更新,有需要的同学可以移步到我的 github 中去查看源码: https://github.com/lichenbuliren/design-mode-notes 1.currying 函数柯里化 currying 又称 部分求值 .一个 cu

JavaScript设计模式与开发实践 – 观察者模式 http://web.jobbole.com/87809/

概述 观察者模式又叫发布 – 订阅模式(Publish/Subscribe),它定义了一种一对多的关系,让多个观察者对象同时监听某一个目标对象(为了方便理解,以下将观察者对象叫做订阅者,将目标对象叫做发布者).发布者的状态发生变化时就会通知所有的订阅者,使得它们能够自动更新自己. 观察者模式的使用场合就是:当一个对象的改变需要同时改变其它对象,并且它不知道具体有多少对象需要改变的时候,就应该考虑使用观察者模式. 观察者模式的中心思想就是促进松散耦合,一为时间上的解耦,二为对象之间的解耦.让耦合的

【摘】JavaScript设计模式与开发实践--单例模式

本文章所有内容均摘自<Javascript设计模式与开发实践>一书(有兴趣的可以购买),加入了一点点自己的理解,写这篇文章的目的是,加强自身对设计模式的理解,以及对于没有接触过这一块的入门者的参考. 阅读本章内容,需要具备Javascript面向对象的知识,否则阅读起来可能会些许困难. 设计模式 单例模式 策略模式 代理模式 迭代器模式 发布-订阅模式 命令模式 组合模式 模板方法模式 享元模式 职责链模式 中介者模式 装饰者模式 状态模式 适配器模式 单例模式 单例模式的定义:保证一个类仅有

JavaScript设计模式与开发实践——JavaScript的多态

"多态"一词源于希腊文polymorphism,拆开来看是poly(复数)+ morph(形态)+ ism,从字面上我们可以理解为复数形态. 多态的实际含义是:同一操作作用于不同的对象上面,可以产生不同的解释和不同的执行结果.换句话说,给不同的对象发送同一个消息的时候,这些对象会根据这个消息分别给出不同的反馈. 从字面上来理解多态不太容易,下面我们来举例说明一下. 主人家里养了两只动物,分别是一只鸭和一只鸡,当主人向它们发出"叫"的命令时,鸭会"嘎嘎嘎&q

JavaScript设计模式与开发实践 – 观察者模式

概述 观察者模式又叫发布 – 订阅模式(Publish/Subscribe),它定义了一种一对多的关系,让多个观察者对象同时监听某一个目标对象(为了方便理解,以下将观察者对象叫做订阅者,将目标对象叫做发布者).发布者的状态发生变化时就会通知所有的订阅者,使得它们能够自动更新自己. 观察者模式的使用场合就是:当一个对象的改变需要同时改变其它对象,并且它不知道具体有多少对象需要改变的时候,就应该考虑使用观察者模式. 观察者模式的中心思想就是促进松散耦合,一为时间上的解耦,二为对象之间的解耦.让耦合的

JavaScript设计模式与开发实践【第一部分】

今天开始阅读<JavaScript设计模式与开发实践>,对于设计模式的学习一直渴望已久. 设计模式的定义是:在面向对象软件设计过程中针对特定问题的简洁而优雅的解决方案. 其实平时在工作中不知不觉在使用某些设计模式,只是我们不知道而已. 动态类型语言和静态类型语言 静态类型语言在编译时便已确定变量的类型,而动态类型语言的变量类型要到程序运行的时 候,待变量被赋予某个值之后,才会具有某种类型. 静态类型语言的优点首先是在编译时就能发现类型不匹配的错误,编辑器可以帮助我们提前 避免程序在运行期间有可

JavaScript设计模式与开发实践——读书笔记1.高阶函数(下)

上部分主要介绍高阶函数的常见形式,本部分将着重介绍高阶函数的高级应用. 1.currying currying指的是函数柯里化,又称部分求值.一个currying的函数会先接受一些参数,但不立即求值,而是继续返回给另一个函数,通过闭包存储起来.等到函数被真正需求要求值的时候,将之前传入的参数统一起来求值.例如,我们要计算一个月的开销,我们并不需要计算每天具体花了多少,而是需要计算月底总共花掉多少,也就是说,实际上我们只需要在月底计算一次.所以每个月的前29天,我们都只需要保存好当天的开销,到30

JavaScript设计模式与开发实践---读书笔记(1)

前言 设计模式的定义是:在面向对象软件设计过程中针对特定问题的简洁而优雅的解决方案. 从某些角度来看,设计模式确实有可能带来代码量的增加,或许会把系统的逻辑搞的更复杂.但软件开发的成本并非全部在开发阶段,设计模式的作用是让人们写出可复用和可维护性高的程序. 所有设计模式的实现都遵循一条原则,即“找出程序中变化的地方,并将变化封装起来”. 不变和稳定的部分是非常容易复用的. 分辨模式的关键是意图而不是结构 模式只有放在具体的环境下才有意义,辨别模式的关键是这个模式出现的场景,以及为我们解决的问题.