PHP设计模式(八)装饰器模式Decorator实例详解【结构型】


Posted in PHP onMay 02, 2020

本文实例讲述了PHP设计模式:装饰器模式Decorator。分享给大家供大家参考,具体如下:

1. 概述

       若你从事过面向对象开发,实现给一个类或对象增加行为,使用继承机制,这是所有面向对象语言的一个基本特性。如果已经存在的一个类缺少某些方法,或者须要给方法添加更多的功能(魅力),你也许会仅仅继承这个类来产生一个新类—这建立在额外的代码上。

      通过继承一个现有类可以使得子类在拥有自身方法的同时还拥有父类的方法。但是这种方法是静态的,用户不能控制增加行为的方式和时机。如果  你希望改变一个已经初始化的对象的行为,你怎么办?或者,你希望继承许多类的行为,改怎么办?前一个,只能在于运行时完成,后者显然时可能的,但是可能会导致产生大量的不同的类—可怕的事情。

2. 问题

你如何组织你的代码使其可以容易的添加基本的或者一些很少用到的 特性,而不是直接不额外的代码写在你的类的内部?

3. 解决方案

        装饰器模式: 动态地给一个对象添加一些额外的职责或者行为。就增加功能来说, Decorator模式相比生成子类更为灵活。

       装饰器模式提供了改变子类的灵活方案。装饰器模式在不必改变原类文件和使用继承的情况下,动态的扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。

       当用于一组子类时,装饰器模式更加有用。如果你拥有一族子类(从一个父类派生而来),你需要在与子类独立使用情况下添加额外的特性,你可以使用装饰器模式,以避免代码重复和具体子类数量的增加。

4. 适用性

以下情况使用Decorator模式

1)• 在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。

2)• 处理那些可以撤消的职责。

3)• 当不能采用生成子类的方法进行扩充时。一种情况是,可能有大量独立的扩展,

为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。

另一种情况可能是因为类定义被隐藏,或类定义不能用于生成子类。

5. 结构

uml如图:

PHP设计模式(八)装饰器模式Decorator实例详解【结构型】

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”带标签的组件—一个输入表单告诉你要输入的内容。因为任何一个表单都可能需要被标记,你可能会象这样继承每一个具体的组件:

PHP设计模式(八)装饰器模式Decorator实例详解【结构型】

上面的类图看起来并不怎么坏,下面让我们再增加一些特性。表单验证阶段,你希望能够指出一个表单控制是否合法。你为非法控制使用的代码又一次继承其它组件,因此又需要产生大量的子类:

PHP设计模式(八)装饰器模式Decorator实例详解【结构型】

这个类看起来并不是太坏,所以让我们增加一些新的功能。在结构有效性确认中你需要指出结构是否是有效的。你需要让你检验有效性的代码也可以应用到其它部件,这样不用再更多的子类上进行有效性验证。

PHP设计模式(八)装饰器模式Decorator实例详解【结构型】

这里子类溢出并不是唯一的问题。想一想那些重复的代码,你需要重新设计你的整个类层次。有没有更好的方法!确实,装饰器模式是避免这种情况的好方法。

装饰器模式结构上类似与代理模式。一个装饰器对象保留有对对象的引用,而且忠实的重新建立被装饰对象的公共接口。装饰器也可以增加方法,扩展被装饰对象的接口,任意重载方法,甚至可以在脚本执行期间有条件的重载方法。

为了探究装饰器模式,让我们以前面讨论过的表单组件库为例,并且用装饰器模式而不是继承,实现“lable”和“invalidation”两个特性。

样本代码:

组件库包含哪些特性?

1.        容易创建表单元素

2.        将表单元素以html方式输出

3.        在每个元素上实现简单的验证

本例中,我们创建一个包含姓,名,邮件地址,输入项的表单。所有的区域都是必须的,而且E-mail必须看起来是有效的E—mail地址。用HTML语言表示,表单的代码象下面所示:

<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设计模式(八)装饰器模式Decorator实例详解【结构型】

我们使用装饰器代码:

<?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表单:

<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
class Invalid extends WidgetDecorator {
 
 function paint() {
 return '<span class="invalid">'.$this->widget->paint().'</span>';
 }
}

FormHandler新加方法validate:

/**
 * 实现
 *
 */
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;
 
 }
}

最后结果:

<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类图展现了这个简洁灵活的解决方案。

希望本文所述对大家PHP程序设计有所帮助。

PHP 相关文章推荐
php,不用COM,生成excel文件
Oct 09 PHP
用PHP+java实现自动新闻滚动窗口
Oct 09 PHP
PHP的中问验证码
Nov 25 PHP
php cookie 登录验证示例代码
Mar 16 PHP
php zip文件解压类代码
Dec 02 PHP
php变量作用域的深入解析
Jun 03 PHP
PHP 实现类似js中alert() 提示框
Mar 18 PHP
PHP中Session可能会引起并发问题
Jun 26 PHP
php验证邮箱和ip地址最简单方法汇总
Oct 30 PHP
PHP框架Laravel中实现supervisor执行异步进程的方法
Jun 07 PHP
PHP操作路由器实现方法示例
Apr 27 PHP
php装饰者模式简单应用案例分析
Oct 23 PHP
PHP设计模式(七)组合模式Composite实例详解【结构型】
May 02 #PHP
PHP设计模式(六)桥连模式Bridge实例详解【结构型】
May 02 #PHP
PHP设计模式(五)适配器模式Adapter实例详解【结构型】
May 02 #PHP
PHP设计模式(四)原型模式Prototype实例详解【创建型】
May 02 #PHP
PHP设计模式(三)建造者模式Builder实例详解【创建型】
May 02 #PHP
PHP设计模式(一)工厂模式Factory实例详解【创建型】
May 02 #PHP
PHP设计模式概论【概念、分类、原则等】
May 01 #PHP
You might like
S900/ ETON E1-XM 收音机
2021/03/02 无线电
WINDOWS 2000下使用ISAPI方式安装PHP
2006/09/05 PHP
非常实用的php弹出错误警告函数扩展性强
2014/01/17 PHP
javascript call和apply方法
2008/11/24 Javascript
js函数使用技巧之 setTimeout(function(){},0)
2009/02/09 Javascript
Javascript Jquery 遍历Json的实现代码
2010/03/31 Javascript
兼容ie、firefox的图片自动缩放的css跟js代码分享
2013/08/12 Javascript
使用Nodejs开发微信公众号后台服务实例
2014/09/03 NodeJs
JavaScript设置表单上传时文件个数的方法
2015/08/11 Javascript
jQuery中常用的遍历函数用法实例总结
2015/09/01 Javascript
JavaScript制作淘宝星级评分效果的思路
2020/06/23 Javascript
JavaScript中Textarea滚动条不能拖动的解决方法
2015/12/15 Javascript
jquery动态遍历Json对象的属性和值的方法
2016/07/27 Javascript
JavaScript用构造函数如何获取变量的类型名
2016/12/23 Javascript
详解vue.js全局组件和局部组件
2017/04/10 Javascript
Vuex之理解Store的用法
2017/04/19 Javascript
JS求解两数之和算法详解
2020/04/28 Javascript
el-table树形表格表单验证(列表生成序号)
2020/05/31 Javascript
微信小程序实现选择地址省市区三级联动
2020/06/21 Javascript
Python实现将目录中TXT合并成一个大TXT文件的方法
2015/07/15 Python
Python常见异常分类与处理方法
2017/06/04 Python
Python中工作日类库Busines Holiday的介绍与使用
2017/07/06 Python
Python常见MongoDB数据库操作实例总结
2018/07/24 Python
django 消息框架 message使用详解
2019/07/22 Python
python threading和multiprocessing模块基本用法实例分析
2019/07/25 Python
Python实现石头剪刀布游戏
2021/01/20 Python
详解Python中openpyxl模块基本用法
2021/02/23 Python
CSS 3.0 结合video视频实现的创意开幕效果
2020/06/01 HTML / CSS
印度领先的在线时尚商店:Koovs
2016/08/28 全球购物
我们没有写servlet的构造方法,那么容器是怎么创建servlet的实例呢
2013/04/24 面试题
党员查摆剖析材料
2014/10/10 职场文书
2014年店长工作总结
2014/11/17 职场文书
社区好人好事材料
2014/12/26 职场文书
工作态度不好检讨书
2015/05/06 职场文书
学校食堂管理制度
2015/08/04 职场文书
2019垃圾分类宣传口号汇总
2019/08/16 职场文书