- 浏览: 797 次
- 性别:
文章分类
最新评论
设计模式之装饰器模式Decorator(结构型)
1. 概述
若你从事过面向对象开发,实现给一个类或对象增加行为,使用继承机制,这是所有面向对象语言的一个基本特性。如果已经存在的一个类缺少某些方法,或者须要给方法添加更多的功能(魅力),你也许会仅仅继承这个类来产生一个新类—这建立在额外的代码上。
通过继承一个现有类可以使得子类在拥有自身方法的同时还拥有父类的方法。但是这种方法是静态的,用户不能控制增加行为的方式和时机。如果 你希望改变一个已经初始化的对象的行为,你怎么办?或者,你希望继承许多类的行为,改怎么办?前一个,只能在于运行时完成,后者显然时可能的,但是可能会导致产生大量的不同的类—可怕的事情。
2. 问题
你如何组织你的代码使其可以容易的添加基本的或者一些很少用到的 特性,而不是直接不额外的代码写在你的类的内部?
3. 解决方案
装饰器模式: 动态地给一个对象添加一些额外的职责或者行为。就增加功能来说, Decorator模式相比生成子类更为灵活。
装饰器模式提供了改变子类的灵活方案。装饰器模式在不必改变原类文件和使用继承的情况下,动态的扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。
当用于一组子类时,装饰器模式更加有用。如果你拥有一族子类(从一个父类派生而来),你需要在与子类独立使用情况下添加额外的特性,你可以使用装饰器模式,以避免代码重复和具体子类数量的增加。
4. 适用性
以下情况使用Decorator模式
1)• 在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
2)• 处理那些可以撤消的职责。
3)• 当不能采用生成子类的方法进行扩充时。一种情况是,可能有大量独立的扩展,
为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。
另一种情况可能是因为类定义被隐藏,或类定义不能用于生成子类。
5.
结构
uml如图:
6.构建模式的组成
抽象组件角色(Component):定义一个对象接口,以规范准备接受附加责任的对象,
即可以给这些对象动态地添加职责。
具体组件角色(ConcreteComponent) :被装饰者,定义一个将要被装饰增加功能的类。
可以给这个类的对象添加一些职责
抽象装饰器(Decorator):维持一个指向构件Component对象的实例,
并定义一个与抽象组件角色Component接口一致的接口
具体装饰器角色(ConcreteDecorator):向组件添加职责。
7. 效果
装饰模式的特点:
(1) 装饰对象和真实对象有相同的接口。这样客户端对象就可以以和真实对象相同的方式和装饰对象交互。
(2) 装饰对象包含一个真实对象的索引(reference)
(3) 装饰对象接受所有的来自客户端的请求。它把这些请求转发给真实的对象。
(4) 装饰对象可以在转发这些请求以前或以后增加一些附加功能。这样就确保了在运行时,不用修改给定对象的结构就可以在外部增加附加的功能。在面向对象的设计中,通常是通过继承来实现对给定类的功能扩展。
Decorator模式至少有两个主要优点和两个缺点:
1) 比静态继承更灵活: 与对象的静态继承(多重继承)相比, Decorator模式提供了更加灵活的向对象添加职责的方式。可以用添加和分离的方法,用装饰在运行时刻增加和删除职责。相比之下,继承机制要求为每个添加的职责创建一个新的子类。这会产生许多新的类,并且会增加系统的复杂度。此外,为一个特定的Component类提供多个不同的 Decorator类,这就使得你可以对一些职责进行混合和匹配。使用Decorator模式可以很容易地重复添加一个特性。
2) 避免在层次结构高层的类有太多的特征 Decorator模式提供了一种“即用即付”的方法来添加职责。它并不试图在一个复杂的可定制的类中支持所有可预见的特征,相反,你可以定义一个简单的类,并且用 Decorator类给它逐渐地添加功能。可以从简单的部件组合出复杂的功能。这样,应用程序不必为不需要的特征付出代价。同时更易于不依赖于 Decorator所扩展(甚至是不可预知的扩展)的类而独立地定义新类型的 Decorator。扩展一个复杂类的时候,很可能会暴露与添加的职责无关的细节。
3) Decorator与它的Component不一样 Decorator是一个透明的包装。如果我们从对象标识的观点出发,一个被装饰了的组件与这个组件是有差别的,因此,使用装饰不应该依赖对象标识。
4) 有许多小对象 采用Decorator模式进行系统设计往往会产生许多看上去类似的小对象,这些对象仅仅在他们相互连接的方式上有所不同,而不是它们的类或是它们的属性值有所不同。尽管对于那些了解这些系统的人来说,很容易对它们进行定制,但是很难学习这些系统,排错也很困难。
8. 实现
使用《php设计模式》里面的例子。
看看以下例子,你可以更好的理解这种观点。考虑一个建立在组件概念上的“form”表单库,在那里你需要为每一个你想要表现的表单控制类型建立一个类。这种类图可以如下所示:
Select and TextInput类是组件类的子类。假如你想要增加一个“labeled”带标签的组件—一个输入表单告诉你要输入的内容。因为任何一个表单都可能需要被标记,你可能会象这样继承每一个具体的组件:
上面的类图看起来并不怎么坏,下面让我们再增加一些特性。表单验证阶段,你希望能够指出一个表单控制是否合法。你为非法控制使用的代码又一次继承其它组件,因此又需要产生大量的子类:
这个类看起来并不是太坏,所以让我们增加一些新的功能。在结构有效性确认中你需要指出结构是否是有效的。你需要让你检验有效性的代码也可以应用到其它部件,这样不用再更多的子类上进行有效性验证。
这里子类溢出并不是唯一的问题。想一想那些重复的代码,你需要重新设计你的整个类层次。有没有更好的方法!确实,装饰器模式是避免这种情况的好方法。
装饰器模式结构上类似与代理模式。一个装饰器对象保留有对对象的引用,而且忠实的重新建立被装饰对象的公共接口。装饰器也可以增加方法,扩展被装饰对象的接口,任意重载方法,甚至可以在脚本执行期间有条件的重载方法。
为了探究装饰器模式,让我们以前面讨论过的表单组件库为例,并且用装饰器模式而不是继承,实现“lable”和“invalidation”两个特性。
样本代码:
组件库包含哪些特性?
1. 容易创建表单元素
2. 将表单元素以html方式输出
3. 在每个元素上实现简单的验证
本例中,我们创建一个包含姓,名,邮件地址,输入项的表单。所有的区域都是必须的,而且E-mail必须看起来是有效的E—mail地址。用HTML语言表示,表单的代码象下面所示:
[html] view plaincopyprint?
01.<form action=”formpage.php” method=”post”>
02.<b>First Name:</b> <input type=”text” name=”fname” value=””><br>
03.<b>Last Name:</b> <input type=”text” name=”lname” value=””><br>
04.<b>Email:</b> <input type=”text” name=”email” value=””><br>
05.<input type=”submit” value=”Submit”>
06.</form>
<form action=”formpage.php” method=”post”>
<b>First Name:</b> <input type=”text” name=”fname” value=””><br>
<b>Last Name:</b> <input type=”text” name=”lname” value=””><br>
<b>Email:</b> <input type=”text” name=”email” value=””><br>
<input type=”submit” value=”Submit”>
</form>
增加一些css样式后,表单渲染出来如下图所示:
我们使用装饰器代码:
[php] view plaincopyprint?
01.<?php
02./**
03. * 装饰器模式的组成:
04. * 抽象组件角色(Component):定义一个对象接口,以规范准备接受附加责任的对象,即可以给这些对象动态地添加职责。
05. * 具体组件角色(ConcreteComponent) :被装饰者,定义一个将要被装饰增加功能的类。可以给这个类的对象添加一些职责。
06. * 抽象装饰器(Decorator):维持一个指向构件Component对象的实例,并定义一个与抽象组件角色Component接口一致的接口。
07. * 具体装饰器角色(ConcreteDecorator): 向组件添加职责。
08. * @author guisu
09. * @version 1.0
10. */
11.
12./**
13. * 抽象组件角色(Component)
14. *
15. */
16.class ComponentWidget {
17. function paint() {
18. return $this->_asHtml();
19. }
20.}
21.
22./**
23. *
24. * 具体组件角色(ConcreteComponent):
25. * 让我们以一个基本的text输入组件开始。它(组件)必须要包含输入区域的名字(name)而且输入内容可以以HTML的方式渲染。
26. *
27. */
28.class ConcreteComponentTextInput extends ComponentWidget {
29.
30. protected $_name;
31. protected $_value;
32.
33. function TextInput($name, $value='') {
34. $this->_name = $name;
35. $this->_value = $value;
36. }
37.
38. function _asHtml() {
39. return '<input type="text" name="'.$this->_name.'" value="'.$this->_value.'">';
40.
41. }
42.
43.}
44./**
45. * 抽象装饰器(Decorator):维持一个指向构件Component对象的实例,并定义一个与抽象组件角色Component接口一致的接口。
46. *
47. * 我们进入有能够统一增加(一些特性)能力的装饰器模式。
48. * 作为开始,我们建立一个普通的可以被扩展产生具体的特定装饰器的WidgetDecorator类。至少WidgetDecorator类应该能够在它的构造函数中接受一个组件,
49. * 并复制公共方法paint()
50. *
51. */
52.class WidgetDecorator {
53.
54. protected $_widget;
55. function __construct( &$widget) {
56. $this->_widget = $widget;
57. }
58. function paint() {
59. return $this->_widget->paint();
60.
61. }
62.
63.}
64./**
65. * 具体装饰器角色(ConcreteDecorator):
66. * 为建立一个标签(lable),需要传入lable的内容,以及原始的组件
67. * 有标签的组件也需要复制paint()方法
68. *
69. */
70.
71.
72.class ConcreteDecoratorLabeled extends WidgetDecorator {
73.
74. protected $_label;
75.
76. function __construct($label, &$widget) {
77. $this->_label = $label;
78. parent::__construct($widget);
79. }
80.
81. function paint() {
82. return '<b>'.$this->_label.':</b> '.$this->_widget->paint();
83. }
84.
85.}
86.
87.
88./**
89. * 实现
90. *
91. */
92.class FormHandler {
93. function build(&$post) {
94. return array(
95. new ConcreteDecoratorLabeled('First Name', new ConcreteComponentTextInput('fname', $post->get('fname')))
96. ,new ConcreteDecoratorLabeled('Last Name', new ConcreteComponentTextInput('lname', $post->get('lname')))
97. ,new ConcreteDecoratorLabeled('Email', new ConcreteComponentTextInput('email', $post->get('email')))
98. );
99.
100. }
101.
102.
103.
104.}
105.
106./**
107. * 通过$_post提交的数据
108. */
109.
110.class Post {
111.
112. private $store = array();
113.
114. function get($key) {
115. if (array_key_exists($key, $this->store))
116. return $this->store[$key];
117. }
118.
119. function set($key, $val) {
120. $this->store[$key] = $val;
121. }
122.
123. static function autoFill() {
124. $ret = new self();
125. foreach($_POST as $key => $value) {
126. $ret->set($key, $value);
127. }
128. return $ret;
129. }
130.
131.}
132.
133.
134.?>
<?php
/**
* 装饰器模式的组成:
* 抽象组件角色(Component):定义一个对象接口,以规范准备接受附加责任的对象,即可以给这些对象动态地添加职责。
* 具体组件角色(ConcreteComponent) :被装饰者,定义一个将要被装饰增加功能的类。可以给这个类的对象添加一些职责。
* 抽象装饰器(Decorator):维持一个指向构件Component对象的实例,并定义一个与抽象组件角色Component接口一致的接口。
* 具体装饰器角色(ConcreteDecorator): 向组件添加职责。
* @author guisu
* @version 1.0
*/
/**
* 抽象组件角色(Component)
*
*/
class ComponentWidget {
function paint() {
return $this->_asHtml();
}
}
/**
*
* 具体组件角色(ConcreteComponent):
* 让我们以一个基本的text输入组件开始。它(组件)必须要包含输入区域的名字(name)而且输入内容可以以HTML的方式渲染。
*
*/
class ConcreteComponentTextInput extends ComponentWidget {
protected $_name;
protected $_value;
function TextInput($name, $value='') {
$this->_name = $name;
$this->_value = $value;
}
function _asHtml() {
return '<input type="text" name="'.$this->_name.'" value="'.$this->_value.'">';
}
}
/**
* 抽象装饰器(Decorator):维持一个指向构件Component对象的实例,并定义一个与抽象组件角色Component接口一致的接口。
*
* 我们进入有能够统一增加(一些特性)能力的装饰器模式。
* 作为开始,我们建立一个普通的可以被扩展产生具体的特定装饰器的WidgetDecorator类。至少WidgetDecorator类应该能够在它的构造函数中接受一个组件,
* 并复制公共方法paint()
*
*/
class WidgetDecorator {
protected $_widget;
function __construct( &$widget) {
$this->_widget = $widget;
}
function paint() {
return $this->_widget->paint();
}
}
/**
* 具体装饰器角色(ConcreteDecorator):
* 为建立一个标签(lable),需要传入lable的内容,以及原始的组件
* 有标签的组件也需要复制paint()方法
*
*/
class ConcreteDecoratorLabeled extends WidgetDecorator {
protected $_label;
function __construct($label, &$widget) {
$this->_label = $label;
parent::__construct($widget);
}
function paint() {
return '<b>'.$this->_label.':</b> '.$this->_widget->paint();
}
}
/**
* 实现
*
*/
class FormHandler {
function build(&$post) {
return array(
new ConcreteDecoratorLabeled('First Name', new ConcreteComponentTextInput('fname', $post->get('fname')))
,new ConcreteDecoratorLabeled('Last Name', new ConcreteComponentTextInput('lname', $post->get('lname')))
,new ConcreteDecoratorLabeled('Email', new ConcreteComponentTextInput('email', $post->get('email')))
);
}
}
/**
* 通过$_post提交的数据
*/
class Post {
private $store = array();
function get($key) {
if (array_key_exists($key, $this->store))
return $this->store[$key];
}
function set($key, $val) {
$this->store[$key] = $val;
}
static function autoFill() {
$ret = new self();
foreach($_POST as $key => $value) {
$ret->set($key, $value);
}
return $ret;
}
}
?>
以创建一个php脚本使用FormHandler类来产生HTML表单:
[php] view plaincopyprint?
01.<form action=”formpage.php” method=”post”>
02.
03.<?php
04.$post =& Post::autoFill();
05.$form = FormHandler::build($post);
06.foreach($form as $widget) {
07. echo $widget->paint(), "<br>\n";
08.}
09.?>
10.
11.<input type=”submit” value=”Submit”>
12.
13.</form>
<form action=”formpage.php” method=”post”>
<?php
$post =& Post::autoFill();
$form = FormHandler::build($post);
foreach($form as $widget) {
echo $widget->paint(), "<br>\n";
}
?>
<input type=”submit” value=”Submit”>
</form>
现在,你已经拥有了个提交给它自身并且能保持posted数据的表单处理(form handler) 类。
现在。我们继续为表单添加一些验证机制。方法是编辑另一个组件装饰器类来表达一个“invalid”状态并扩展FormHandler类增加一个validate()方法以处理组件示例数组。如果组件非法(“invalid”),我们通过一个“invalid”类将它包装在<span>元素中。
[php] view plaincopyprint?
01.<?php
02.
03.class Invalid extends WidgetDecorator {
04.
05. function paint() {
06. return '<span class="invalid">'.$this->widget->paint().'</span>';
07. }
08.}
<?php
class Invalid extends WidgetDecorator {
function paint() {
return '<span class="invalid">'.$this->widget->paint().'</span>';
}
}
FormHandler新加方法validate:
[php] view plaincopyprint?
01./**
02. * 实现
03. *
04. */
05.class FormHandler {
06. function build(&$post) {
07. return array(
08. new ConcreteDecoratorLabeled('First Name', new ConcreteComponentTextInput('fname', $post->get('fname')))
09. ,new ConcreteDecoratorLabeled('Last Name', new ConcreteComponentTextInput('lname', $post->get('lname')))
10. ,new ConcreteDecoratorLabeled('Email', new ConcreteComponentTextInput('email', $post->get('email')))
11. );
12.
13. }
14.
15. function validate(&$form, &$post) {
16. $valid = true;
17. // first name required
18. if (!strlen($post->get('fname'))) {
19. $form[0] =& new Invalid($form[0]);
20. $valid = false;
21. }
22.
23. // last name required
24. if (!strlen($post->get('lname'))) {
25. $form[1] =& new Invalid($form[1]);
26. $valid = false;}
27. // email has to look real
28. if (!preg_match('~\w+@(\w+\.)+\w+~'
29. ,$post->get('email'))) {
30. $form[2] =& new Invalid($form[2]);
31. $valid = false;
32. }
33. return $valid;
34.
35. }
36.
37.}
/**
* 实现
*
*/
class FormHandler {
function build(&$post) {
return array(
new ConcreteDecoratorLabeled('First Name', new ConcreteComponentTextInput('fname', $post->get('fname')))
,new ConcreteDecoratorLabeled('Last Name', new ConcreteComponentTextInput('lname', $post->get('lname')))
,new ConcreteDecoratorLabeled('Email', new ConcreteComponentTextInput('email', $post->get('email')))
);
}
function validate(&$form, &$post) {
$valid = true;
// first name required
if (!strlen($post->get('fname'))) {
$form[0] =& new Invalid($form[0]);
$valid = false;
}
// last name required
if (!strlen($post->get('lname'))) {
$form[1] =& new Invalid($form[1]);
$valid = false;}
// email has to look real
if (!preg_match('~\w+@(\w+\.)+\w+~'
,$post->get('email'))) {
$form[2] =& new Invalid($form[2]);
$valid = false;
}
return $valid;
}
}
最后结果:
[php] view plaincopyprint?
01.<html>
02.
03.<head>
04.<title>Decorator Example</title>
05.<style type="text/css">
06..invalid {color: red; }
07..invalid input { background-color: red; color: yellow; }
08.#myform input { position: absolute; left: 110px; width: 250px; font-weight: bold;}
09.</style>
10.</head>
11.<body>
12.<form action="<?php echo $_SERVER["PHP_SELF"]; ?>" method="post">
13.<div id="myform">
14.<?php
15.$pos =& Post::autoFill();
16.$form = FormHandler::build($post);
17.if ($_POST) { FormHandler::validate($form, $post);
18.}
19.foreach($form as $widget) {
20. echo $widget->paint(), "<br>\n";
21.}
22.?>
23.
24.</div>
25.<input type="submit" value="Submit">
26.</form>
27.</body>
28.</html>
<html>
<head>
<title>Decorator Example</title>
<style type="text/css">
.invalid {color: red; }
.invalid input { background-color: red; color: yellow; }
#myform input { position: absolute; left: 110px; width: 250px; font-weight: bold;}
</style>
</head>
<body>
<form action="<?php echo $_SERVER["PHP_SELF"]; ?>" method="post">
<div id="myform">
<?php
$pos =& Post::autoFill();
$form = FormHandler::build($post);
if ($_POST) { FormHandler::validate($form, $post);
}
foreach($form as $widget) {
echo $widget->paint(), "<br>\n";
}
?>
</div>
<input type="submit" value="Submit">
</form>
</body>
</html>
9. 装饰器模式与其他相关模式
1)Adapter 模式:Decorator模式不同于Adapter模式,因为装饰仅改变对象的职责而
不改变它的接口;而适配器将给对象一个全新的接口。
2)Composite模式:可以将装饰视为一个退化的、仅有一个组件的组
合。然而,装饰仅给对象添加一些额外的职责—它的目的不在于对象聚集。
3)Strategy模式:用一个装饰你可以改变对象的外表;而Strategy模
式使得你可以改变对象的内核。这是改变对象的两种途径。
10.总结
1)使用装饰器设计模式设计类的目标是:
不必重写任何已有的功能性代码,而是对某个基于对象应用增量变化。
2)
装饰器设计模式采用这样的构建方式: 在主代码流中应该能够直接插入一个或多个更改或“装饰”目标对象的装饰器,
同时不影响其他代码流。
3)
Decorator模式采用对象组合而非继承的手法,实现了在运行时动态的扩展对象功能的能力,
而且可以根据需要扩展多个功能,避免了单独使用继承带来的“灵活性差”和“多子类衍生问题”。
同时它很好地符合面向对象设计原则中“优先使用对象组合而非继承”和“开放-封闭”原则。
也许装饰器模式最重要的一个方面是它的超过继承的能力。“问题”部分展现了一个使用继承的子类爆炸。
基于装饰器模式的解决方案,UML类图展现了这个简洁灵活的解决方案。
转载于:http://blog.csdn.net/hguisu/article/details/7531960
若你从事过面向对象开发,实现给一个类或对象增加行为,使用继承机制,这是所有面向对象语言的一个基本特性。如果已经存在的一个类缺少某些方法,或者须要给方法添加更多的功能(魅力),你也许会仅仅继承这个类来产生一个新类—这建立在额外的代码上。
通过继承一个现有类可以使得子类在拥有自身方法的同时还拥有父类的方法。但是这种方法是静态的,用户不能控制增加行为的方式和时机。如果 你希望改变一个已经初始化的对象的行为,你怎么办?或者,你希望继承许多类的行为,改怎么办?前一个,只能在于运行时完成,后者显然时可能的,但是可能会导致产生大量的不同的类—可怕的事情。
2. 问题
你如何组织你的代码使其可以容易的添加基本的或者一些很少用到的 特性,而不是直接不额外的代码写在你的类的内部?
3. 解决方案
装饰器模式: 动态地给一个对象添加一些额外的职责或者行为。就增加功能来说, Decorator模式相比生成子类更为灵活。
装饰器模式提供了改变子类的灵活方案。装饰器模式在不必改变原类文件和使用继承的情况下,动态的扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。
当用于一组子类时,装饰器模式更加有用。如果你拥有一族子类(从一个父类派生而来),你需要在与子类独立使用情况下添加额外的特性,你可以使用装饰器模式,以避免代码重复和具体子类数量的增加。
4. 适用性
以下情况使用Decorator模式
1)• 在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
2)• 处理那些可以撤消的职责。
3)• 当不能采用生成子类的方法进行扩充时。一种情况是,可能有大量独立的扩展,
为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。
另一种情况可能是因为类定义被隐藏,或类定义不能用于生成子类。
5.
结构
uml如图:
6.构建模式的组成
抽象组件角色(Component):定义一个对象接口,以规范准备接受附加责任的对象,
即可以给这些对象动态地添加职责。
具体组件角色(ConcreteComponent) :被装饰者,定义一个将要被装饰增加功能的类。
可以给这个类的对象添加一些职责
抽象装饰器(Decorator):维持一个指向构件Component对象的实例,
并定义一个与抽象组件角色Component接口一致的接口
具体装饰器角色(ConcreteDecorator):向组件添加职责。
7. 效果
装饰模式的特点:
(1) 装饰对象和真实对象有相同的接口。这样客户端对象就可以以和真实对象相同的方式和装饰对象交互。
(2) 装饰对象包含一个真实对象的索引(reference)
(3) 装饰对象接受所有的来自客户端的请求。它把这些请求转发给真实的对象。
(4) 装饰对象可以在转发这些请求以前或以后增加一些附加功能。这样就确保了在运行时,不用修改给定对象的结构就可以在外部增加附加的功能。在面向对象的设计中,通常是通过继承来实现对给定类的功能扩展。
Decorator模式至少有两个主要优点和两个缺点:
1) 比静态继承更灵活: 与对象的静态继承(多重继承)相比, Decorator模式提供了更加灵活的向对象添加职责的方式。可以用添加和分离的方法,用装饰在运行时刻增加和删除职责。相比之下,继承机制要求为每个添加的职责创建一个新的子类。这会产生许多新的类,并且会增加系统的复杂度。此外,为一个特定的Component类提供多个不同的 Decorator类,这就使得你可以对一些职责进行混合和匹配。使用Decorator模式可以很容易地重复添加一个特性。
2) 避免在层次结构高层的类有太多的特征 Decorator模式提供了一种“即用即付”的方法来添加职责。它并不试图在一个复杂的可定制的类中支持所有可预见的特征,相反,你可以定义一个简单的类,并且用 Decorator类给它逐渐地添加功能。可以从简单的部件组合出复杂的功能。这样,应用程序不必为不需要的特征付出代价。同时更易于不依赖于 Decorator所扩展(甚至是不可预知的扩展)的类而独立地定义新类型的 Decorator。扩展一个复杂类的时候,很可能会暴露与添加的职责无关的细节。
3) Decorator与它的Component不一样 Decorator是一个透明的包装。如果我们从对象标识的观点出发,一个被装饰了的组件与这个组件是有差别的,因此,使用装饰不应该依赖对象标识。
4) 有许多小对象 采用Decorator模式进行系统设计往往会产生许多看上去类似的小对象,这些对象仅仅在他们相互连接的方式上有所不同,而不是它们的类或是它们的属性值有所不同。尽管对于那些了解这些系统的人来说,很容易对它们进行定制,但是很难学习这些系统,排错也很困难。
8. 实现
使用《php设计模式》里面的例子。
看看以下例子,你可以更好的理解这种观点。考虑一个建立在组件概念上的“form”表单库,在那里你需要为每一个你想要表现的表单控制类型建立一个类。这种类图可以如下所示:
Select and TextInput类是组件类的子类。假如你想要增加一个“labeled”带标签的组件—一个输入表单告诉你要输入的内容。因为任何一个表单都可能需要被标记,你可能会象这样继承每一个具体的组件:
上面的类图看起来并不怎么坏,下面让我们再增加一些特性。表单验证阶段,你希望能够指出一个表单控制是否合法。你为非法控制使用的代码又一次继承其它组件,因此又需要产生大量的子类:
这个类看起来并不是太坏,所以让我们增加一些新的功能。在结构有效性确认中你需要指出结构是否是有效的。你需要让你检验有效性的代码也可以应用到其它部件,这样不用再更多的子类上进行有效性验证。
这里子类溢出并不是唯一的问题。想一想那些重复的代码,你需要重新设计你的整个类层次。有没有更好的方法!确实,装饰器模式是避免这种情况的好方法。
装饰器模式结构上类似与代理模式。一个装饰器对象保留有对对象的引用,而且忠实的重新建立被装饰对象的公共接口。装饰器也可以增加方法,扩展被装饰对象的接口,任意重载方法,甚至可以在脚本执行期间有条件的重载方法。
为了探究装饰器模式,让我们以前面讨论过的表单组件库为例,并且用装饰器模式而不是继承,实现“lable”和“invalidation”两个特性。
样本代码:
组件库包含哪些特性?
1. 容易创建表单元素
2. 将表单元素以html方式输出
3. 在每个元素上实现简单的验证
本例中,我们创建一个包含姓,名,邮件地址,输入项的表单。所有的区域都是必须的,而且E-mail必须看起来是有效的E—mail地址。用HTML语言表示,表单的代码象下面所示:
[html] view plaincopyprint?
01.<form action=”formpage.php” method=”post”>
02.<b>First Name:</b> <input type=”text” name=”fname” value=””><br>
03.<b>Last Name:</b> <input type=”text” name=”lname” value=””><br>
04.<b>Email:</b> <input type=”text” name=”email” value=””><br>
05.<input type=”submit” value=”Submit”>
06.</form>
<form action=”formpage.php” method=”post”>
<b>First Name:</b> <input type=”text” name=”fname” value=””><br>
<b>Last Name:</b> <input type=”text” name=”lname” value=””><br>
<b>Email:</b> <input type=”text” name=”email” value=””><br>
<input type=”submit” value=”Submit”>
</form>
增加一些css样式后,表单渲染出来如下图所示:
我们使用装饰器代码:
[php] view plaincopyprint?
01.<?php
02./**
03. * 装饰器模式的组成:
04. * 抽象组件角色(Component):定义一个对象接口,以规范准备接受附加责任的对象,即可以给这些对象动态地添加职责。
05. * 具体组件角色(ConcreteComponent) :被装饰者,定义一个将要被装饰增加功能的类。可以给这个类的对象添加一些职责。
06. * 抽象装饰器(Decorator):维持一个指向构件Component对象的实例,并定义一个与抽象组件角色Component接口一致的接口。
07. * 具体装饰器角色(ConcreteDecorator): 向组件添加职责。
08. * @author guisu
09. * @version 1.0
10. */
11.
12./**
13. * 抽象组件角色(Component)
14. *
15. */
16.class ComponentWidget {
17. function paint() {
18. return $this->_asHtml();
19. }
20.}
21.
22./**
23. *
24. * 具体组件角色(ConcreteComponent):
25. * 让我们以一个基本的text输入组件开始。它(组件)必须要包含输入区域的名字(name)而且输入内容可以以HTML的方式渲染。
26. *
27. */
28.class ConcreteComponentTextInput extends ComponentWidget {
29.
30. protected $_name;
31. protected $_value;
32.
33. function TextInput($name, $value='') {
34. $this->_name = $name;
35. $this->_value = $value;
36. }
37.
38. function _asHtml() {
39. return '<input type="text" name="'.$this->_name.'" value="'.$this->_value.'">';
40.
41. }
42.
43.}
44./**
45. * 抽象装饰器(Decorator):维持一个指向构件Component对象的实例,并定义一个与抽象组件角色Component接口一致的接口。
46. *
47. * 我们进入有能够统一增加(一些特性)能力的装饰器模式。
48. * 作为开始,我们建立一个普通的可以被扩展产生具体的特定装饰器的WidgetDecorator类。至少WidgetDecorator类应该能够在它的构造函数中接受一个组件,
49. * 并复制公共方法paint()
50. *
51. */
52.class WidgetDecorator {
53.
54. protected $_widget;
55. function __construct( &$widget) {
56. $this->_widget = $widget;
57. }
58. function paint() {
59. return $this->_widget->paint();
60.
61. }
62.
63.}
64./**
65. * 具体装饰器角色(ConcreteDecorator):
66. * 为建立一个标签(lable),需要传入lable的内容,以及原始的组件
67. * 有标签的组件也需要复制paint()方法
68. *
69. */
70.
71.
72.class ConcreteDecoratorLabeled extends WidgetDecorator {
73.
74. protected $_label;
75.
76. function __construct($label, &$widget) {
77. $this->_label = $label;
78. parent::__construct($widget);
79. }
80.
81. function paint() {
82. return '<b>'.$this->_label.':</b> '.$this->_widget->paint();
83. }
84.
85.}
86.
87.
88./**
89. * 实现
90. *
91. */
92.class FormHandler {
93. function build(&$post) {
94. return array(
95. new ConcreteDecoratorLabeled('First Name', new ConcreteComponentTextInput('fname', $post->get('fname')))
96. ,new ConcreteDecoratorLabeled('Last Name', new ConcreteComponentTextInput('lname', $post->get('lname')))
97. ,new ConcreteDecoratorLabeled('Email', new ConcreteComponentTextInput('email', $post->get('email')))
98. );
99.
100. }
101.
102.
103.
104.}
105.
106./**
107. * 通过$_post提交的数据
108. */
109.
110.class Post {
111.
112. private $store = array();
113.
114. function get($key) {
115. if (array_key_exists($key, $this->store))
116. return $this->store[$key];
117. }
118.
119. function set($key, $val) {
120. $this->store[$key] = $val;
121. }
122.
123. static function autoFill() {
124. $ret = new self();
125. foreach($_POST as $key => $value) {
126. $ret->set($key, $value);
127. }
128. return $ret;
129. }
130.
131.}
132.
133.
134.?>
<?php
/**
* 装饰器模式的组成:
* 抽象组件角色(Component):定义一个对象接口,以规范准备接受附加责任的对象,即可以给这些对象动态地添加职责。
* 具体组件角色(ConcreteComponent) :被装饰者,定义一个将要被装饰增加功能的类。可以给这个类的对象添加一些职责。
* 抽象装饰器(Decorator):维持一个指向构件Component对象的实例,并定义一个与抽象组件角色Component接口一致的接口。
* 具体装饰器角色(ConcreteDecorator): 向组件添加职责。
* @author guisu
* @version 1.0
*/
/**
* 抽象组件角色(Component)
*
*/
class ComponentWidget {
function paint() {
return $this->_asHtml();
}
}
/**
*
* 具体组件角色(ConcreteComponent):
* 让我们以一个基本的text输入组件开始。它(组件)必须要包含输入区域的名字(name)而且输入内容可以以HTML的方式渲染。
*
*/
class ConcreteComponentTextInput extends ComponentWidget {
protected $_name;
protected $_value;
function TextInput($name, $value='') {
$this->_name = $name;
$this->_value = $value;
}
function _asHtml() {
return '<input type="text" name="'.$this->_name.'" value="'.$this->_value.'">';
}
}
/**
* 抽象装饰器(Decorator):维持一个指向构件Component对象的实例,并定义一个与抽象组件角色Component接口一致的接口。
*
* 我们进入有能够统一增加(一些特性)能力的装饰器模式。
* 作为开始,我们建立一个普通的可以被扩展产生具体的特定装饰器的WidgetDecorator类。至少WidgetDecorator类应该能够在它的构造函数中接受一个组件,
* 并复制公共方法paint()
*
*/
class WidgetDecorator {
protected $_widget;
function __construct( &$widget) {
$this->_widget = $widget;
}
function paint() {
return $this->_widget->paint();
}
}
/**
* 具体装饰器角色(ConcreteDecorator):
* 为建立一个标签(lable),需要传入lable的内容,以及原始的组件
* 有标签的组件也需要复制paint()方法
*
*/
class ConcreteDecoratorLabeled extends WidgetDecorator {
protected $_label;
function __construct($label, &$widget) {
$this->_label = $label;
parent::__construct($widget);
}
function paint() {
return '<b>'.$this->_label.':</b> '.$this->_widget->paint();
}
}
/**
* 实现
*
*/
class FormHandler {
function build(&$post) {
return array(
new ConcreteDecoratorLabeled('First Name', new ConcreteComponentTextInput('fname', $post->get('fname')))
,new ConcreteDecoratorLabeled('Last Name', new ConcreteComponentTextInput('lname', $post->get('lname')))
,new ConcreteDecoratorLabeled('Email', new ConcreteComponentTextInput('email', $post->get('email')))
);
}
}
/**
* 通过$_post提交的数据
*/
class Post {
private $store = array();
function get($key) {
if (array_key_exists($key, $this->store))
return $this->store[$key];
}
function set($key, $val) {
$this->store[$key] = $val;
}
static function autoFill() {
$ret = new self();
foreach($_POST as $key => $value) {
$ret->set($key, $value);
}
return $ret;
}
}
?>
以创建一个php脚本使用FormHandler类来产生HTML表单:
[php] view plaincopyprint?
01.<form action=”formpage.php” method=”post”>
02.
03.<?php
04.$post =& Post::autoFill();
05.$form = FormHandler::build($post);
06.foreach($form as $widget) {
07. echo $widget->paint(), "<br>\n";
08.}
09.?>
10.
11.<input type=”submit” value=”Submit”>
12.
13.</form>
<form action=”formpage.php” method=”post”>
<?php
$post =& Post::autoFill();
$form = FormHandler::build($post);
foreach($form as $widget) {
echo $widget->paint(), "<br>\n";
}
?>
<input type=”submit” value=”Submit”>
</form>
现在,你已经拥有了个提交给它自身并且能保持posted数据的表单处理(form handler) 类。
现在。我们继续为表单添加一些验证机制。方法是编辑另一个组件装饰器类来表达一个“invalid”状态并扩展FormHandler类增加一个validate()方法以处理组件示例数组。如果组件非法(“invalid”),我们通过一个“invalid”类将它包装在<span>元素中。
[php] view plaincopyprint?
01.<?php
02.
03.class Invalid extends WidgetDecorator {
04.
05. function paint() {
06. return '<span class="invalid">'.$this->widget->paint().'</span>';
07. }
08.}
<?php
class Invalid extends WidgetDecorator {
function paint() {
return '<span class="invalid">'.$this->widget->paint().'</span>';
}
}
FormHandler新加方法validate:
[php] view plaincopyprint?
01./**
02. * 实现
03. *
04. */
05.class FormHandler {
06. function build(&$post) {
07. return array(
08. new ConcreteDecoratorLabeled('First Name', new ConcreteComponentTextInput('fname', $post->get('fname')))
09. ,new ConcreteDecoratorLabeled('Last Name', new ConcreteComponentTextInput('lname', $post->get('lname')))
10. ,new ConcreteDecoratorLabeled('Email', new ConcreteComponentTextInput('email', $post->get('email')))
11. );
12.
13. }
14.
15. function validate(&$form, &$post) {
16. $valid = true;
17. // first name required
18. if (!strlen($post->get('fname'))) {
19. $form[0] =& new Invalid($form[0]);
20. $valid = false;
21. }
22.
23. // last name required
24. if (!strlen($post->get('lname'))) {
25. $form[1] =& new Invalid($form[1]);
26. $valid = false;}
27. // email has to look real
28. if (!preg_match('~\w+@(\w+\.)+\w+~'
29. ,$post->get('email'))) {
30. $form[2] =& new Invalid($form[2]);
31. $valid = false;
32. }
33. return $valid;
34.
35. }
36.
37.}
/**
* 实现
*
*/
class FormHandler {
function build(&$post) {
return array(
new ConcreteDecoratorLabeled('First Name', new ConcreteComponentTextInput('fname', $post->get('fname')))
,new ConcreteDecoratorLabeled('Last Name', new ConcreteComponentTextInput('lname', $post->get('lname')))
,new ConcreteDecoratorLabeled('Email', new ConcreteComponentTextInput('email', $post->get('email')))
);
}
function validate(&$form, &$post) {
$valid = true;
// first name required
if (!strlen($post->get('fname'))) {
$form[0] =& new Invalid($form[0]);
$valid = false;
}
// last name required
if (!strlen($post->get('lname'))) {
$form[1] =& new Invalid($form[1]);
$valid = false;}
// email has to look real
if (!preg_match('~\w+@(\w+\.)+\w+~'
,$post->get('email'))) {
$form[2] =& new Invalid($form[2]);
$valid = false;
}
return $valid;
}
}
最后结果:
[php] view plaincopyprint?
01.<html>
02.
03.<head>
04.<title>Decorator Example</title>
05.<style type="text/css">
06..invalid {color: red; }
07..invalid input { background-color: red; color: yellow; }
08.#myform input { position: absolute; left: 110px; width: 250px; font-weight: bold;}
09.</style>
10.</head>
11.<body>
12.<form action="<?php echo $_SERVER["PHP_SELF"]; ?>" method="post">
13.<div id="myform">
14.<?php
15.$pos =& Post::autoFill();
16.$form = FormHandler::build($post);
17.if ($_POST) { FormHandler::validate($form, $post);
18.}
19.foreach($form as $widget) {
20. echo $widget->paint(), "<br>\n";
21.}
22.?>
23.
24.</div>
25.<input type="submit" value="Submit">
26.</form>
27.</body>
28.</html>
<html>
<head>
<title>Decorator Example</title>
<style type="text/css">
.invalid {color: red; }
.invalid input { background-color: red; color: yellow; }
#myform input { position: absolute; left: 110px; width: 250px; font-weight: bold;}
</style>
</head>
<body>
<form action="<?php echo $_SERVER["PHP_SELF"]; ?>" method="post">
<div id="myform">
<?php
$pos =& Post::autoFill();
$form = FormHandler::build($post);
if ($_POST) { FormHandler::validate($form, $post);
}
foreach($form as $widget) {
echo $widget->paint(), "<br>\n";
}
?>
</div>
<input type="submit" value="Submit">
</form>
</body>
</html>
9. 装饰器模式与其他相关模式
1)Adapter 模式:Decorator模式不同于Adapter模式,因为装饰仅改变对象的职责而
不改变它的接口;而适配器将给对象一个全新的接口。
2)Composite模式:可以将装饰视为一个退化的、仅有一个组件的组
合。然而,装饰仅给对象添加一些额外的职责—它的目的不在于对象聚集。
3)Strategy模式:用一个装饰你可以改变对象的外表;而Strategy模
式使得你可以改变对象的内核。这是改变对象的两种途径。
10.总结
1)使用装饰器设计模式设计类的目标是:
不必重写任何已有的功能性代码,而是对某个基于对象应用增量变化。
2)
装饰器设计模式采用这样的构建方式: 在主代码流中应该能够直接插入一个或多个更改或“装饰”目标对象的装饰器,
同时不影响其他代码流。
3)
Decorator模式采用对象组合而非继承的手法,实现了在运行时动态的扩展对象功能的能力,
而且可以根据需要扩展多个功能,避免了单独使用继承带来的“灵活性差”和“多子类衍生问题”。
同时它很好地符合面向对象设计原则中“优先使用对象组合而非继承”和“开放-封闭”原则。
也许装饰器模式最重要的一个方面是它的超过继承的能力。“问题”部分展现了一个使用继承的子类爆炸。
基于装饰器模式的解决方案,UML类图展现了这个简洁灵活的解决方案。
转载于:http://blog.csdn.net/hguisu/article/details/7531960
相关推荐
装饰者模式(Decorator Pattern)是结构型设计模式之一,它允许在运行时向对象添加新的行为或职责,而无需修改对象的源代码。这个模式的名字来源于装饰艺术,它通过添加额外的装饰来增强一个物体的外观,同样地,...
装饰模式(Decorator Pattern)是一种结构型设计模式,它在不改变原有对象的基础上,通过包裹一个对象并为其添加新的行为或责任,实现对对象功能的扩展。这种模式在软件开发中非常常见,尤其当需要在运行时动态改变...
装饰器模式(Decorator Pattern)是一种结构型设计模式,主要用于在运行时动态地给对象添加新的职责或行为,而不必改变现有对象的类定义。在面向对象编程中,装饰器模式提供了一种相对于继承更加灵活的方式来增强或...
装饰模式(Decorator Pattern)是一种结构型设计模式,允许在不改变对象接口的情况下,动态地为对象添加额外的职责或功能。装饰模式通常用于需要扩展对象功能而又不希望使用子类化的场景。 装饰模式的组成 组件接口...
装饰模式(Decorator Pattern)是设计模式中的一种结构型模式,它在不改变原有对象的基础上,通过添加额外的职责来扩展对象的功能。在C#中,装饰模式尤其适用于那些需要动态地增加或减少对象功能的情况,避免了使用...
装饰模式(Decorator)是软件设计模式中的一种结构型模式,其主要目的是在不改变对象原有类的基础上,通过添加新的行为或职责来扩展对象的功能。这种模式使得代码的扩展性非常优秀,避免了由于频繁地使用继承而导致...
装饰器模式(Decorator)是一种设计模式,它允许在运行时向对象添加新的行为或责任,而无需修改对象的源代码。这种模式属于结构型模式,是面向对象设计中的一种非常实用的技术。 装饰器模式的核心思想是通过将一个...
c++设计模式-结构型模式-装饰器模式;QT工程;c++简单源码; 装饰器(Decorator)模式的定义:指在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)的模式,它属于对象结构型模式。
装饰器(Decorator)模式是一种典型的结构型模式,主要用意是动态地为对象添加一些额外的功能。它提供了一个灵活的替代方案来继承子类,以扩展对象的功能。 在《Element of Reusable Object-Oriented Software》中...
装饰模式(Decorator)是软件设计领域中一种非常实用的结构型设计模式,它允许我们向一个对象添加新的行为或责任,而无需修改该对象的源代码。在C++编程语言中,装饰模式常用于动态地扩展类的功能,使得类的行为在...
装饰模式(Decorator Pattern)是一种结构型设计模式,它允许你向一个现有的对象添加新的功能,同时又不改变其结构。装饰模式通过创建一个装饰类,该类包装了原始类的实例,并在调用原始类方法之前或之后添加额外的...
装饰器模式(Decorator)是Java设计模式中的一种结构型模式,它的主要目的是为了在不修改原有对象的基础上,通过添加额外的职责来扩展对象的功能。这种模式可以看作是一种灵活的替代继承来实现功能扩展的方式。 在...
装饰器模式是一种结构型设计模式,它允许在不修改对象本身的情况下动态地为对象添加新的行为或职责。这种模式在软件工程中广泛应用,特别是在需要扩展已有功能而不影响原有代码结构时。在iOS开发中,装饰器模式同样...
装饰器模式(Decorator Pattern)是结构型设计模式之一,用于动态地向对象添加额外的职责,而不影响其他对象。这种模式通过使用装饰器函数,允许你在保持原始功能的基础上,灵活地扩展代码的行为。
Decorator模式是一种结构型设计模式,它的主要作用是为对象添加额外的功能,而无需修改对象的源代码。Decorator模式的核心在于动态地将责任附加到对象上,通过封装来增加新的行为或职责,使得装饰者和组件(被装饰...
【Java设计模式之装饰器模式】装饰器模式是一种结构型设计模式,它的主要目的是在不修改已有对象的前提下,通过添加额外的职责来扩展对象的功能。这种模式遵循单一职责原则,使得扩展职责时不会破坏原有对象的结构。...
装饰者模式是一种结构型设计模式,它允许我们在运行时给对象添加新的行为或职责,而无需修改其原有代码。这种模式遵循开闭原则,即对扩展开放,对修改关闭。 装饰者模式的核心思想是通过包装(decorating)原始对象...
### C#面向对象设计模式纵横谈(10):Decorator 装饰模式(结构型模式) #### 一、背景介绍 在面向对象编程中,设计模式是一种在特定情况下解决问题的标准化方法。其中,装饰模式(Decorator Pattern)作为结构型模式...