| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -168,31 +168,30 @@ | |||
| 168 | 168 | - [Function.prototype.bind()](javascript.patterns/blob/master/chapter6.markdown#a25) | |
| 169 | 169 | - [小结](javascript.patterns/blob/master/chapter6.markdown#a26) | |
| 170 | 170 | ||
| 171 | - ## 第七章 设计模式 | ||
| 172 | - | ||
| 173 | - - 单体 | ||
| 174 | - - 使用 new | ||
| 175 | - - 静态属性中的实例 | ||
| 176 | - - 闭包中的实例 | ||
| 177 | - - 工厂 | ||
| 178 | - - 内置对象工厂 | ||
| 179 | - - 迭代器 | ||
| 180 | - - 装饰者 | ||
| 181 | - - 用法 | ||
| 182 | - - 实现 | ||
| 183 | - - 使用列表实现 | ||
| 184 | - - 策略 | ||
| 185 | - - 数据校验的例子 | ||
| 186 | - - 外观 | ||
| 187 | - - 代理 | ||
| 188 | - - 一个例子 | ||
| 189 | - - 作为缓存的代理 | ||
| 190 | - - 中介者 | ||
| 191 | - - 中介者例子 | ||
| 192 | - - 观察者 | ||
| 193 | - - 例子 1:杂志订阅 | ||
| 194 | - - 例子 2:按键游戏 | ||
| 195 | - - 小节 | ||
| 171 | + ## [第七章 设计模式](javascript.patterns/blob/master/chapter7.markdown#a1) | ||
| 172 | + | ||
| 173 | + - [单例](javascript.patterns/blob/master/chapter7.markdown#a2) | ||
| 174 | + - [使用new](javascript.patterns/blob/master/chapter7.markdown#a3) | ||
| 175 | + - [将实例放到静态属性中](javascript.patterns/blob/master/chapter7.markdown#a4) | ||
| 176 | + - [将实例放到闭包中](javascript.patterns/blob/master/chapter7.markdown#a5) | ||
| 177 | + - [工厂模式](javascript.patterns/blob/master/chapter7.markdown#a6) | ||
| 178 | + - [内置对象工厂](javascript.patterns/blob/master/chapter7.markdown#a7) | ||
| 179 | + - [迭代器](javascript.patterns/blob/master/chapter7.markdown#a8) | ||
| 180 | + - [装饰器](javascript.patterns/blob/master/chapter7.markdown#a9) | ||
| 181 | + - [用法](javascript.patterns/blob/master/chapter7.markdown#a10) | ||
| 182 | + - [实现](javascript.patterns/blob/master/chapter7.markdown#a11) | ||
| 183 | + - [使用列表实现](javascript.patterns/blob/master/chapter7.markdown#a12) | ||
| 184 | + - [策略模式](javascript.patterns/blob/master/chapter7.markdown#a13) | ||
| 185 | + - [数据验证示例](javascript.patterns/blob/master/chapter7.markdown#a14) | ||
| 186 | + - [外观模式](javascript.patterns/blob/master/chapter7.markdown#a15) | ||
| 187 | + - [代理模式](javascript.patterns/blob/master/chapter7.markdown#a16) | ||
| 188 | + - [一个例子](javascript.patterns/blob/master/chapter7.markdown#a17) | ||
| 189 | + - [中介者模式](javascript.patterns/blob/master/chapter7.markdown#a18) | ||
| 190 | + - [中介者示例](javascript.patterns/blob/master/chapter7.markdown#a19) | ||
| 191 | + - [观察者模式](javascript.patterns/blob/master/chapter7.markdown#a20) | ||
| 192 | + - [例1:杂志订阅](javascript.patterns/blob/master/chapter7.markdown#a21) | ||
| 193 | + - [例2:按键游戏](javascript.patterns/blob/master/chapter7.markdown#a22) | ||
| 194 | + - [小结](javascript.patterns/blob/master/chapter7.markdown#a23) | ||
| 196 | 195 | ||
| 197 | 196 | ## 第八章 DOM和浏览器模式 | |
| 198 | 197 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -9,6 +9,7 @@ JavaScript作为一种基于原型的弱类型动态语言,使得有些时候 | |||
| 9 | 9 | ||
| 10 | 10 | 让我们从第一个例子——单例模式——来看一下在JavaScript中和静态的基于类的语言有什么不同。 | |
| 11 | 11 | ||
| 12 | + <a name="a2"></a> | ||
| 12 | 13 | ## 单例 | |
| 13 | 14 | ||
| 14 | 15 | 单例模式的核心思想是让指定的类只存在唯一一个实例。这意味着当你第二次使用相同的类去创建对象的时候,你得到的应该和第一次创建的是同一个对象。 | |
@@ -31,6 +32,7 @@ JavaScript作为一种基于原型的弱类型动态语言,使得有些时候 | |||
| 31 | 32 | ||
| 32 | 33 | > 需要注意的是,有的时候当人们在JavaScript中提出“单例”的时候,它们可能是在指第5章讨论过的“模块模式”。 | |
| 33 | 34 | ||
| 35 | + <a name="a3"></a> | ||
| 34 | 36 | ### 使用new | |
| 35 | 37 | ||
| 36 | 38 | JavaScript没有类,所以一字一句地说单例的定义并没有什么意义。但是JavaScript有使用new、通过构造函数来创建对象的语法,有时候你可能需要这种语法下的一个单例实现。这也就是说当你使用new、通过同一个构造函数来创建多个对象的时候,你应该只是得到同一个对象的不同引用。 | |
@@ -53,6 +55,7 @@ JavaScript没有类,所以一字一句地说单例的定义并没有什么意 | |||
| 53 | 55 | ||
| 54 | 56 | 让我们来看一下第二种和第三种方案的实现示例。 | |
| 55 | 57 | ||
| 58 | + <a name="a4"></a> | ||
| 56 | 59 | ### 将实例放到静态属性中 | |
| 57 | 60 | ||
| 58 | 61 | 下面是一个将唯一的实例放入Universe构造函数的一个静态属性中的例子: | |
@@ -82,7 +85,8 @@ JavaScript没有类,所以一字一句地说单例的定义并没有什么意 | |||
| 82 | 85 | ||
| 83 | 86 | 如你所见,这是一种直接有效的解决方案,唯一的缺陷是instance是可被公开访问的。一般来说它被其它代码误删改的可能是很小的(起码比全局变量instance要小得多),但是仍然是有可能的。 | |
| 84 | 87 | ||
| 85 | - ### 将实例放入闭包中 | ||
| 88 | + <a name="a5"></a> | ||
| 89 | + ### 将实例放到闭包中 | ||
| 86 | 90 | ||
| 87 | 91 | 另一种实现基于类的单例模式的方法是使用一个闭包来保护这个唯一的实例。你可以通过第5章讨论过的“私有静态成员模式”来实现。唯一的秘密就是重写构造函数: | |
| 88 | 92 | ||
@@ -210,6 +214,7 @@ uni.constructor不再和Universe()相同的原因是uni.constructor仍然是指 | |||
| 210 | 214 | ||
| 211 | 215 | }()); | |
| 212 | 216 | ||
| 217 | + <a name="a6"></a> | ||
| 213 | 218 | ## 工厂模式 | |
| 214 | 219 | ||
| 215 | 220 | 使用工厂模式的目的就是创建对象。它通常被在类或者类的静态方法中实现,目的是: | |
@@ -289,6 +294,7 @@ uni.constructor不再和Universe()相同的原因是uni.constructor仍然是指 | |||
| 289 | 294 | ||
| 290 | 295 | 工厂模式的实现中没有什么是特别困难的。你需要做的仅仅是寻找请求类型的对象的构造函数。在这个例子中,使用了一个简单的名字转换以便映射对象类型和创建对象的构造函数。继承的部分只是一个公共的重复代码片段的示例,它可以被放到工厂方法中而不是被每个构造函数的类型重复。(译注:指通过原型继承的代码可以在factory方法以外执行,而不是放到factory中每调用一次都要执行一次。) | |
| 291 | 296 | ||
| 297 | + <a name="a7"></a> | ||
| 292 | 298 | ### 内置对象工厂 | |
| 293 | 299 | ||
| 294 | 300 | 作为一个“野生的工厂”的例子,我们来看一下内置的全局构造函数Object()。它的行为很像工厂,因为它根据不同的输入创建不同的对象。如果传入一个数字,它会使用Number()构造函数创建一个对象。在传入字符串和布尔值的时候也会发生同样的事情。任何其它的值(包括空值)将会创建一个正常的对象。 | |
@@ -308,6 +314,8 @@ uni.constructor不再和Universe()相同的原因是uni.constructor仍然是指 | |||
| 308 | 314 | ||
| 309 | 315 | Object()也是一个工厂这一事实可能没有太多实际用处,仅仅是觉得值得作为一个例子提一下,告诉我们工厂模式是随处可见的。 | |
| 310 | 316 | ||
| 317 | + | ||
| 318 | + <a name="a8"></a> | ||
| 311 | 319 | ## 迭代器 | |
| 312 | 320 | ||
| 313 | 321 | 在迭代器模式中,你有一些含有有序聚合数据的对象。这些数据可能在内部用一种复杂的结构存储着,但是你希望提供一种简单的方法来访问这种结构中的每个元素。数据的使用者不需要知道你是怎样组织你的数据的,他们只需要操作一个个独立的元素。 | |
@@ -329,12 +337,14 @@ Object()也是一个工厂这一事实可能没有太多实际用处,仅仅是 | |||
| 329 | 337 | console.log(agg.next()); | |
| 330 | 338 | } | |
| 331 | 339 | ||
| 340 | + <a name="a9"></a> | ||
| 332 | 341 | ## 装饰器 | |
| 333 | 342 | ||
| 334 | 343 | 在装饰器模式中,一些额外的功能可以在运行时被动态地添加到一个对象中。在静态的基于类的语言中,处理这个问题可能是个挑战,但是在JavaScript中,对象本来就是可变的,所以给一个对象添加额外的功能本身并不是什么问题。 | |
| 335 | 344 | ||
| 336 | 345 | 装饰器模式的一个很方便的特性是可以对我们需要的特性进行定制和配置。刚开始时,我们有一个拥有基本功能的对象,然后可以从可用的装饰器中去挑选一些需要用到的去增加这个对象,甚至如果顺序很重要的话,还可以指定增强的顺序。 | |
| 337 | 346 | ||
| 347 | + <a name="a10"></a> | ||
| 338 | 348 | ### 用法 | |
| 339 | 349 | ||
| 340 | 350 | 我们来看一下这个模式的示例用法。假设你正在做一个卖东西的web应用,每个新交易是一个新的sale对象。这个对象“知道”交易的价格并且可以通过调用sale.getPrice()方法返回。根据环境的不同,你可以开始用一些额外的功能来装饰这个对象。假设一个场景是这笔交易是发生在加拿大的一个省Québec,在这种情况下,购买者需要付联邦税和Québec省税。根据装饰器模式的用法,你需要指明使用联邦税装饰器和Québec省税装饰器来装饰这个对象。然后你还可以给这个对象装饰一些价格格式的功能。这个场景的使用方式可能是像这样: | |
@@ -354,6 +364,7 @@ Object()也是一个工厂这一事实可能没有太多实际用处,仅仅是 | |||
| 354 | 364 | ||
| 355 | 365 | 如你所见,这是一种在运行时很灵活的方法来添加功能和调整对象。我们来看一下如何来实现这种模式。 | |
| 356 | 366 | ||
| 367 | + <a name="a11"></a> | ||
| 357 | 368 | ### 实现 | |
| 358 | 369 | ||
| 359 | 370 | 一种实现装饰器模式的方法是让每个装饰器成为一个拥有应该被重写的方法的对象。每个装饰器实际上是继承自已经被前一个装饰器增强过的对象。装饰器的每个方法都会调用父对象(继承自的对象)的同名方法并取得值,然后做一些额外的处理。 | |
@@ -429,6 +440,7 @@ Object()也是一个工厂这一事实可能没有太多实际用处,仅仅是 | |||
| 429 | 440 | return newobj; | |
| 430 | 441 | }; | |
| 431 | 442 | ||
| 443 | + <a name="a12"></a> | ||
| 432 | 444 | ### 使用列表实现 | |
| 433 | 445 | ||
| 434 | 446 | 我们来看另一个明显不同的实现方法,受益于JavaScript的动态特性,它完全不需要使用继承。同时,我们也可以简单地将前一个方面的结果作为参数传给下一个方法,而不需要每一个方法都去调用前一个方法。 | |
@@ -492,6 +504,7 @@ Sale()构造函数现在有了一个作为自己属性的装饰器列表: | |||
| 492 | 504 | ||
| 493 | 505 | 装饰器模式的第二种实现方式更简单一些,并且没有引入继承。装饰的方法也会简单。所有的工作都由“同意”被装饰的方法来做。在这个示例实现中,getPrice()是唯一被允许装饰的方法。如果你想有更多可以被装饰的方法,那遍历装饰器列表的工作就需要由每个方法重复去做。但是,这可以很容易地被抽象到一个辅助方法中,给它传一个方法然后使这个方法“可被装饰”。如果这样实现的话,decorators_list属性就应该是一个对象,它的属性名字是方法名,值是装饰器对象的数组。 | |
| 494 | 506 | ||
| 507 | + <a name="a13"></a> | ||
| 495 | 508 | ## 策略模式 | |
| 496 | 509 | ||
| 497 | 510 | 策略模式允许在运行的时候选择算法。你的代码的使用者可以在处理特定任务的时候根据即将要做的事情的上下文来从一些可用的算法中选择一个。 | |
@@ -500,6 +513,7 @@ Sale()构造函数现在有了一个作为自己属性的装饰器列表: | |||
| 500 | 513 | ||
| 501 | 514 | 但是根据具体的需要验证的表单和数据,你代码的使用者可以选择进行不同类别的检查。你的validator选择最佳的策略来处理这个任务,然后将具体的数据检查工作交给合适的算法去做。 | |
| 502 | 515 | ||
| 516 | + <a name="a14"></a> | ||
| 503 | 517 | ### 数据验证示例 | |
| 504 | 518 | ||
| 505 | 519 | 假设你有一个下面这样的数据,它可能来自页面上的一个表单,你希望验证它是不是有效的数据: | |
@@ -617,6 +631,7 @@ Sale()构造函数现在有了一个作为自己属性的装饰器列表: | |||
| 617 | 631 | ||
| 618 | 632 | 如你所见,validator对象是通用的,在所有的需要验证的场景下都可以保持这个样子。改进它的办法就是增加更多类型的检查。如果你将它用在很多页面上,每快你就会有一个非常好的验证类型的集合。然后在每个新的使用场景下你需要做的仅仅是配置validator然后调用validate()方法。 | |
| 619 | 633 | ||
| 634 | + <a name="a15"></a> | ||
| 620 | 635 | ## 外观模式 | |
| 621 | 636 | ||
| 622 | 637 | 外观模式是一种很简单的模式,它只是为对象提供了更多的可供选择的接口。使方法保持短小而不是处理太多的工作是一种很好的实践。在这种实践的指导下,你会有一大堆的方法,而不是一个有着非常多参数的uber方法。有些时候,两个或者更多的方法会经常被一起调用。在这种情况下,创建另一个将这些重复调用包裹起来的方法就变得意义了。 | |
@@ -666,6 +681,7 @@ Sale()构造函数现在有了一个作为自己属性的装饰器列表: | |||
| 666 | 681 | ||
| 667 | 682 | 外观模式在做一些重新设计和重构工作时也很有用。当你想用一个不同的实现来替换某个对象的时候,你可能需要工作相当长一段时间(一个复杂的对象),与此同时,一些使用这个新对象的代码也在被同步编写。你可以先想好新对象的API,然后使用新的API创建一个外观方法在旧的对象前面。使用这种方式,当你完全替换到旧的对象的时候,你只需要修改少量客户代码,因为新的客户代码已经是在使用新的API了。 | |
| 668 | 683 | ||
| 684 | + <a name="a16"></a> | ||
| 669 | 685 | ## 代理模式 | |
| 670 | 686 | ||
| 671 | 687 | 在代理设计模式中,一个对象充当了另一个对象的接口的角色。它和外观模式不一样,外观模式带来的方便仅限于将几个方法调用联合起来。而代理对象位于某个对象和它的客户之间,可以保护对对象的访问。 | |
@@ -680,6 +696,7 @@ Sale()构造函数现在有了一个作为自己属性的装饰器列表: | |||
| 680 | 696 | ||
| 681 | 697 | 图7-2 通过代理对象时客户代码与真正的主体的关系 | |
| 682 | 698 | ||
| 699 | + <a name="a17"></a> | ||
| 683 | 700 | ### 一个例子 | |
| 684 | 701 | ||
| 685 | 702 | 在真正的主体做某件工作开销很大时,代理模式很有用处。在web应用中,开销最大的操作之一就是网络请求,此时尽可能地合并HTTP请求是有意义的。我们来看一个这种场景下应用代理模式的实例。 | |
@@ -916,7 +933,7 @@ proxy对象创建了一个队列来收集50ms之内接受到的视频ID,然后 | |||
| 916 | 933 | ||
| 917 | 934 | 图7-6 代理缓存 | |
| 918 | 935 | ||
| 919 | - | ||
| 936 | + <a name="a18"></a> | ||
| 920 | 937 | ## 中介者模式 | |
| 921 | 938 | ||
| 922 | 939 | 一个应用不论大小,都是由一些彼此独立的对象组成的。所有的对象都需要一个通讯的方式来保持可维护性,即你可以安全地修改应用的一部分而不破坏其它部分。随着应用的开发和维护,会有越来越多的对象。然后,在重构代码的时候,对象可能会被移除或者被重新安排。当对象知道其它对象的太多信息并且直接通讯(直接调用彼此的方法或者修改属性)时,会导致我们不愿意看到的紧耦合。当对象耦合很紧时,要修改一个对象而不影响其它的对象是很困难的。此时甚至连一个最简单的修改都变得不那么容易,甚至连一个修改需要用多长时间都难以评估。 | |
@@ -927,6 +944,7 @@ proxy对象创建了一个队列来收集50ms之内接受到的视频ID,然后 | |||
| 927 | 944 | ||
| 928 | 945 | 图7-7 中介者模式中的对象关系 | |
| 929 | 946 | ||
| 947 | + <a name="a19"></a> | ||
| 930 | 948 | ### 中介者示例 | |
| 931 | 949 | ||
| 932 | 950 | 我们来看一个使用中介者模式的实例。这个应用是一个游戏,它的玩法是比较两位游戏者在半分钟内按下按键的次数,次数多的获胜。玩家1需要按的是1,玩家2需要按的是0(这样他们的手指不会搅在一起)。当前分数会显示在一个计分板上。 | |
@@ -1035,12 +1053,14 @@ scoreboard对象(计分板)有一个update()方法,它会在每次玩家 | |||
| 1035 | 1053 | }, 30000); | |
| 1036 | 1054 | ||
| 1037 | 1055 | ||
| 1056 | + <a name="a20"></a> | ||
| 1038 | 1057 | ## 观察者模式 | |
| 1039 | 1058 | ||
| 1040 | 1059 | 观察者模式被广泛地应用于JavaScript客户端编程中。所有的浏览器事件(mouseover,keypress等)都是使用观察者模式的例子。这种模式的另一个名字叫“自定义事件”,意思是这些事件是被编写出来的,和浏览器触发的事件相对。它还有另外一个名字叫“订阅者/发布者”模式。 | |
| 1041 | 1060 | ||
| 1042 | 1061 | 使用这个模式的最主要目的就是促进代码触解耦。在观察者模式中,一个对象订阅另一个对象的指定活动并得到通知,而不是调用另一个对象的方法。订阅者也被叫作观察者,被观察的对象叫作发布者或者被观察者(译注:subject,不知道如何翻译,第一次的时候译为“主体”,第二次译时觉得不妥,还是直接叫被观察者好了)。当一个特定的事件发生的时候,发布者会通知(调用)所有的订阅者,同时还可能以事件对象的形式传递一些消息。 | |
| 1043 | 1062 | ||
| 1063 | + <a name="a21"></a> | ||
| 1044 | 1064 | ### 例1:杂志订阅 | |
| 1045 | 1065 | ||
| 1046 | 1066 | 为了理解观察者模式的实现方式,我们来看一个具体的例子。我们假设有一个发布者paper,它发行一份日报和一份月刊。无论是日报还是月刊发行,有一个名叫joe的订阅者都会收到通知。 | |
@@ -1184,6 +1204,7 @@ paper对象也可以提供unsubscribe()方法,它可以将订阅者从数组 | |||
| 1184 | 1204 | ||
| 1185 | 1205 | 你可以在<http://jspatterns.com/book/7/observer.html>看到完整的源代码,并且在控制台中运行这个实例。 | |
| 1186 | 1206 | ||
| 1207 | + <a name="a22"></a> | ||
| 1187 | 1208 | ### 例2:按键游戏 | |
| 1188 | 1209 | ||
| 1189 | 1210 | 我们来看另一个例子。我们将实现一个和中介者模式的示例一样的按钮游戏,但这次使用观察者模式。为了让它看起来更高档,我们允许接受无限个玩家,而不限于2个。我们仍然保留用来产生玩家的Player()构造函数,也保留scoreboard对象。只有mediator会变成game对象。 | |
@@ -1321,6 +1342,7 @@ while (1) { | |||
| 1321 | 1342 | ||
| 1322 | 1343 | 值得注意的是,在中介者模式中,mediator对象必须知道所有的对象,然后在适当的时机去调用对应的方法。而这个例子中,game对象会显得笨一些(译注:指知道的信息少一些),游戏依赖于对象去观察特写的事件然后触发相应的动作:如scoreboard观察scorechange事件。这使得对象之间的耦合更松了(对象间知道彼此的信息越少越好),而代价则是弄清事件和订阅者之间的对应关系会更困难一些。在这个例子中,所有的订阅行为都发生在代码中的同一个地方,而随着应用规模的境长,on()可能会被在各个地方调用(如在每个对象的初始化代码中)。这使得调试更困难一些,因为没有一个集中的地方来看这些代码并理解正在发生什么事情。在观察者模式中,你将不再能看到那种从开头一直跟到结尾的顺序执行方式。 | |
| 1323 | 1344 | ||
| 1345 | + <a name="a23"></a> | ||
| 1324 | 1346 | ## 小结 | |
| 1325 | 1347 | ||
| 1326 | 1348 | 在这章中你学习到了若干种流行的设计模式,并且也知道了如何在JavaScript中实现它们。我们讨论过的设计模式有: | |
| Back | FazBrowse Home | New Git URL |
0 commit comments