Skip to content

Commit d36dc03

Browse files
committed
装饰器 校对完毕
1 parent 07d5e55 commit d36dc03

1 file changed

Lines changed: 25 additions & 29 deletions

File tree

chapter7.markdown

Lines changed: 25 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -325,39 +325,36 @@ JavaScript没有类,所以一字一句地说单例的定义并没有什么意
325325
console.log(agg.next());
326326
}
327327

328-
<a name="a9"></a>
329328
## 装饰器
330329

331330
在装饰器模式中,一些额外的功能可以在运行时被动态地添加到一个对象中。在静态的基于类的语言中,处理这个问题可能是个挑战,但是在JavaScript中,对象本来就是可变的,所以给一个对象添加额外的功能本身并不是什么问题。
332331

333-
装饰器模式的一个很方便的特性是可以对我们需要的特性进行定制和配置。刚开始时,我们有一个拥有基本功能的对象,然后可以从可用的装饰器中去挑选一些需要用到的去增加这个对象,甚至如果顺序很重要的话,还可以指定增强的顺序。
332+
装饰器模式的一个很方便的特性是可以对我们需要的特性进行定制和配置。刚开始时,我们有一个拥有基本功能的对象,然后可以从可用的装饰器中去挑选一些需要用到的去增强这个对象,如果有必要的话,还可以指定增强的顺序。
334333

335-
<a name="a10"></a>
336334
### 用法
337335

338-
我们来看一下这个模式的示例用法。假设你正在做一个卖东西的web应用,每个新交易是一个新的sale对象。这个对象“知道”交易的价格并且可以通过调用sale.getPrice()方法返回。根据环境的不同,你可以开始用一些额外的功能来装饰这个对象。假设一个场景是这笔交易是发生在加拿大的一个省Québec,在这种情况下,购买者需要付联邦税和Québec省税。根据装饰器模式的用法,你需要指明使用联邦税装饰器和Québec省税装饰器来装饰这个对象。然后你还可以给这个对象装饰一些价格格式的功能。这个场景的使用方式可能是像这样:
336+
我们来看一下这个模式的用法示例。假设你正在做一个卖东西的web应用,每个新交易是一个新的`sale`对象。这个对象“知道”交易的价格并且可以通过调用`sale.getPrice()`方法返回。根据环境的不同,你可以开始用一些额外的功能来装饰这个对象。假设一个场景是这笔交易是发生在加拿大的一个省Québec,在这种情况下,购买者需要付联邦税和Québec省税。根据装饰器模式的用法,你需要指明使用联邦税装饰器和Québec省税装饰器来装饰这个对象。然后你还可以给这个对象装饰一些价格格式的功能。这个场景的使用方式可能是像这样:
339337

340-
var sale = new Sale(100); // the price is 100 dollars
341-
sale = sale.decorate('fedtax'); // add federal tax
342-
sale = sale.decorate('quebec'); // add provincial tax
343-
sale = sale.decorate('money'); // format like money
338+
var sale = new Sale(100); // 价格是100美元
339+
sale = sale.decorate('fedtax'); // 加上联邦税
340+
sale = sale.decorate('quebec'); // 加上省税
341+
sale = sale.decorate('money'); // 格式化
344342
sale.getPrice(); // "$112.88"
345343

346344
在另一种场景下,购买者在一个不需要交省税的省,并且你想用加拿大元的格式来显示价格,你可以这样做:
347345

348-
var sale = new Sale(100); // the price is 100 dollars
349-
sale = sale.decorate('fedtax'); // add federal tax
350-
sale = sale.decorate('cdn'); // format using CDN
346+
var sale = new Sale(100); // 价格是100美元
347+
sale = sale.decorate('fedtax'); // 加上联邦税
348+
sale = sale.decorate('cdn'); // 用加拿大元格式化
351349
sale.getPrice(); // "CDN$ 105.00"
352350

353-
如你所见,这是一种在运行时很灵活的方法来添加功能和调整对象。我们来看一下如何来实现这种模式。
351+
如你所见,这种方法可以在运行时很灵活地添加功能和调整对象。我们来看一下如何来实现这种模式。
354352

355-
<a name="a11"></a>
356353
### 实现
357354

358355
一种实现装饰器模式的方法是让每个装饰器成为一个拥有应该被重写的方法的对象。每个装饰器实际上是继承自已经被前一个装饰器增强过的对象。装饰器的每个方法都会调用父对象(继承自的对象)的同名方法并取得值,然后做一些额外的处理。
359356

360-
最终的效果就是当你在第一个例子中调用sale.getPrice()时,实际上是在调用money装饰器的方法(图7-1)。但是因为每个装饰器会先调用父对象的方法,money的getPrice()先调用quebec的getPrice(),而它又会去调用fedtax的getPrice()方法,依次类推。这个链会一直走到原始的未经装饰的由Sale()构造函数实现的getPrice()
357+
最终的效果就是当你在第一个例子中调用`sale.getPrice()`时,实际上是在调用`money`装饰器的方法(图7-1)。但是因为每个装饰器会先调用父对象的方法,`money``getPrice()`先调用`quebec``getPrice()`,而它又会去调用`fedtax``getPrice()`方法,依次类推。这个链会一直走到原始的未经装饰的由`Sale()`构造函数实现的`getPrice()`
361358

362359
![图7-1 装饰器模式的实现](./Figure/chapter7/7-1.jpg)
363360
图7-1 装饰器模式的实现
@@ -375,7 +372,7 @@ JavaScript没有类,所以一字一句地说单例的定义并没有什么意
375372

376373
Sale.decorators = {};
377374

378-
我们来看一个装饰器的例子。这是一个对象,实现了一个自定义的getPrice()方法。注意这个方法首先从父对象的方法中取值然后修改这个值:
375+
我们来看一个装饰器的例子。这是一个对象,实现了一个自定义的`getPrice()`方法。注意这个方法首先从父对象的方法中取值然后修改这个值:
379376

380377
Sale.decorators.fedtax = {
381378
getPrice: function () {
@@ -385,7 +382,7 @@ JavaScript没有类,所以一字一句地说单例的定义并没有什么意
385382
}
386383
};
387384

388-
使用类似的方法我们可以实现任意多个需要的其它装饰器。他们的实现方式像插件一样来扩展核心的Sale()的功能。他们甚至可以被放到额外的文件中,被第三方的开发者来开发和共享:
385+
使用类似的方法我们可以实现任意多个需要的装饰器。它们的实现方式像插件一样来扩展核心的`Sale()`的功能。它们甚至可以被放到额外的文件中,被第三方的开发者来开发和共享:
389386

390387
Sale.decorators.quebec = {
391388
getPrice: function () {
@@ -407,11 +404,11 @@ JavaScript没有类,所以一字一句地说单例的定义并没有什么意
407404
}
408405
};
409406

410-
最后我们来看decorate()这个神奇的方法,它把所有上面说的片段都串起来了。记得它是这样被调用的
407+
最后我们来看`decorate()`这个神奇的方法,它把所有上面说的片段都串起来了。记住它是这样被调用的
411408

412409
sale = sale.decorate('fedtax');
413410

414-
字符串'fedtax'对应在Sale.decorators.fedtax中实现的对象。被装饰过的最新的对象newobj将从现在有的对象(也就是this对象,它要么是原始的对象,要么是经过最后一个装饰器装饰过的对象)中继承。实现这一部分需要用到前面章节中提到的临时构造函数模式。我们也设置一个uber属性给newobj以便子对象可以访问到父对象。然后我们从装饰器中复制所有额外的属性到被装饰的对象newobj中。最后,在我们的例子中,newobj被返回并且成为被更新过的sale对象
411+
字符串`'fedtax'`对应在`Sale.decorators.fedtax`中实现的对象。被装饰过的最新的对象`newobj`将从现在有的对象(也就是`this`对象,它要么是原始的对象,要么是经过最后一个装饰器装饰过的对象)中继承。实现这一部分需要用到前面章节中提到的临时构造函数模式。我们也设置一个`uber`属性给`newobj`以便子对象可以访问到父对象。然后我们从装饰器中复制所有额外的属性到被装饰的对象`newobj`。最后,在我们的例子中,`newobj`被返回并且成为被更新过的`sale`对象
415412

416413
Sale.prototype.decorate = function (decorator) {
417414
var F = function () {},
@@ -428,29 +425,28 @@ JavaScript没有类,所以一字一句地说单例的定义并没有什么意
428425
return newobj;
429426
};
430427

431-
<a name="a12"></a>
432428
### 使用列表实现
433429

434-
我们来看另一个明显不同的实现方法,受益于JavaScript的动态特性,它完全不需要使用继承。同时,我们也可以简单地将前一个方面的结果作为参数传给下一个方法,而不需要每一个方法都去调用前一个方法。
430+
我们来看另一个明显不同的实现方法,得益于JavaScript的动态特性,它完全不需要使用继承。同时,我们也可以简单地将前一个方面的结果作为参数传给下一个方法,而不需要每一个方法都去调用前一个方法。
435431

436432
这样的实现方法还允许很容易地反装饰(undecorating)或者撤销一个装饰,这仅仅需要从一个装饰器列表中移除一个条目。
437433

438-
用法示例也会明显简单一些,因为我们不需要将decorate()的返回值赋值给对象。在这个实现中,decorate()不对对象做任何事情,它只是简单地将装饰器加入到一个列表中:
434+
用法示例也会明显简单一些,因为我们不需要将`decorate()`的返回值赋值给对象。在这个实现中,`decorate()`不对对象做任何事情,它只是简单地将装饰器加入到一个列表中:
439435

440-
var sale = new Sale(100); // the price is 100 dollars
441-
sale.decorate('fedtax'); // add federal tax
442-
sale.decorate('quebec'); // add provincial tax
443-
sale.decorate('money'); // format like money
436+
var sale = new Sale(100); // 价格是100美元
437+
sale.decorate('fedtax'); // 加上联邦税
438+
sale.decorate('quebec'); // 加上省税
439+
sale.decorate('money'); // 格式化
444440
sale.getPrice(); // "$112.88"
445441

446-
Sale()构造函数现在有了一个作为自己属性的装饰器列表
442+
`Sale()`构造函数现在有了一个作为自己属性存在的装饰器列表
447443

448444
function Sale(price) {
449445
this.price = price || 100;
450446
this.decorators_list = [];
451447
}
452448

453-
可用的装饰器仍然被实现为Sale.decorators的属性。注意getPrice()方法现在更简单了,因为它们不需要调用父对象的getPrice()来获取结果,结果已经作为参数传递给它们了:
449+
可用的装饰器仍然被实现为`Sale.decorators`的属性。注意`getPrice()`方法现在更简单了,因为它们不需要调用父对象的`getPrice()`来获取结果,结果已经作为参数传递给它们了:
454450

455451
Sale.decorators = {};
456452

@@ -472,7 +468,7 @@ Sale()构造函数现在有了一个作为自己属性的装饰器列表:
472468
}
473469
};
474470

475-
最有趣的部分发生在父对象的decorate()和getPrice()方法上。在前一种实现方式中,decorate()还是多少有些复杂,而getPrice()十分简单。在这种实现方式中事情反过来了:decorate()只需要往列表中添加条目而getPrice()做了所有的工作。这些工作包括遍历现在添加的装饰器的列表,然后调用它们的getPrice()方法,并将结果传递给前一个
471+
最有趣的部分发生在父对象的`decorate()``getPrice()`方法上。在前一种实现方式中,`decorate()`还是多少有些复杂,`getPrice()`十分简单。在这种实现方式中事情反过来了:`decorate()`只需要往列表中添加条目而`getPrice()`做了其它所有的工作,包括遍历现在添加的装饰器的列表,然后调用它们的`getPrice()`方法并将结果传递下去
476472

477473
Sale.prototype.decorate = function (decorator) {
478474
this.decorators_list.push(decorator);
@@ -490,7 +486,7 @@ Sale()构造函数现在有了一个作为自己属性的装饰器列表:
490486
return price;
491487
};
492488

493-
装饰器模式的第二种实现方式更简单一些,并且没有引入继承。装饰的方法也会简单。所有的工作都由“同意”被装饰的方法来做。在这个示例实现中,getPrice()是唯一被允许装饰的方法。如果你想有更多可以被装饰的方法,那遍历装饰器列表的工作就需要由每个方法重复去做。但是,这可以很容易地被抽象到一个辅助方法中,给它传一个方法然后使这个方法“可被装饰”。如果这样实现的话,decorators_list属性就应该是一个对象,它的属性名字是方法名,值是装饰器对象的数组。
489+
装饰器模式的第二种实现方式更简单一些,并且没有引入继承。装饰的方法也会简单。所有的工作都由“同意”被装饰的方法来做。在这个示例实现中,`getPrice()`是唯一被允许装饰的方法。如果你想有更多可以被装饰的方法,那遍历装饰器列表的工作就需要由每个方法重复去做。但是,这可以很容易地被抽象到一个辅助方法中,给它传一个方法然后使这个方法“可被装饰”。如果这样实现的话,`decorators_list`属性就应该是一个对象,它的属性名字是方法名,值是装饰器对象的数组。
494490

495491
<a name="a13"></a>
496492
## 策略模式

0 commit comments

Comments
 (0)