PHP设计模式(八)装饰器模式Decorator实例详解【结构型】 |
本文实例讲述了PHP设计模式:装饰器模式Decorator 。分享给大家供大家参考,具体如下: 1. 概述若你从事过面向对象开发,实现给一个类或对象增加行为,使用继承机制,这是所有面向对象语言的一个基本特性 。如果已经存在的一个类缺少某些方法,或者须要给方法添加更多的功能(魅力),你也许会仅仅继承这个类来产生一个新类—这建立在额外的代码上 。 通过继承一个现有类可以使得子类在拥有自身方法的同时还拥有父类的方法 。但是这种方法是静态的,用户不能控制增加行为的方式和时机 。如果 你希望改变一个已经初始化的对象的行为,你怎么办?或者,你希望继承许多类的行为,改怎么办?前一个,只能在于运行时完成,后者显然时可能的,但是可能会导致产生大量的不同的类—可怕的事情 。 2. 问题你如何组织你的代码使其可以容易的添加基本的或者一些很少用到的 特性,而不是直接不额外的代码写在你的类的内部? 3. 解决方案装饰器模式: 动态地给一个对象添加一些额外的职责或者行为 。就增加功能来说, Decorator模式相比生成子类更为灵活 。 装饰器模式提供了改变子类的灵活方案 。装饰器模式在不必改变原类文件和使用继承的情况下,动态的扩展一个对象的功能 。它是通过创建一个包装对象,也就是装饰来包裹真实的对象 。 当用于一组子类时,装饰器模式更加有用 。如果你拥有一族子类(从一个父类派生而来),你需要在与子类独立使用情况下添加额外的特性,你可以使用装饰器模式,以避免代码重复和具体子类数量的增加 。 4. 适用性以下情况使用Decorator模式 1)• 在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责 。 2)• 处理那些可以撤消的职责 。 3)• 当不能采用生成子类的方法进行扩充时 。一种情况是,可能有大量独立的扩展, 为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长 。 另一种情况可能是因为类定义被隐藏,或类定义不能用于生成子类 。 5. 结构uml如图: 6.构建模式的组成抽象组件角色(Component):定义一个对象接口,以规范准备接受附加责任的对象, 即可以给这些对象动态地添加职责 。 具体组件角色(ConcreteComponent) :被装饰者,定义一个将要被装饰增加功能的类 。 可以给这个类的对象添加一些职责 抽象装饰器(Decorator):维持一个指向构件Component对象的实例, 并定义一个与抽象组件角色Component接口一致的接口 具体装饰器角色(ConcreteDecorator):向组件添加职责 。 7. 效果装饰模式的特点: (1) 装饰对象和真实对象有相同的接口 。这样客户端对象就可以以和真实对象相同的方式和装饰对象交互 。 Decorator模式至少有两个主要优点和两个缺点: 1) 比静态继承更灵活: 与对象的静态继承(多重继承)相比, Decorator模式提供了更加灵活的向对象添加职责的方式 。可以用添加和分离的方法,用装饰在运行时刻增加和删除职责 。相比之下,继承机制要求为每个添加的职责创建一个新的子类 。这会产生许多新的类,并且会增加系统的复杂度 。此外,为一个特定的Component类提供多个不同的 Decorator类,这就使得你可以对一些职责进行混合和匹配 。使用Decorator模式可以很容易地重复添加一个特性 。 8. 实现使用《php设计模式》里面的例子 。 看看以下例子,你可以更好的理解这种观点 。考虑一个建立在组件概念上的“form”表单库,在那里你需要为每一个你想要表现的表单控制类型建立一个类 。这种类图可以如下所示: Select and TextInput类是组件类的子类 。假如你想要增加一个“labeled”带标签的组件—一个输入表单告诉你要输入的内容 。因为任何一个表单都可能需要被标记,你可能会象这样继承每一个具体的组件: 上面的类图看起来并不怎么坏,下面让我们再增加一些特性 。表单验证阶段,你希望能够指出一个表单控制是否合法 。你为非法控制使用的代码又一次继承其它组件,因此又需要产生大量的子类: 这个类看起来并不是太坏,所以让我们增加一些新的功能 。在结构有效性确认中你需要指出结构是否是有效的 。你需要让你检验有效性的代码也可以应用到其它部件,这样不用再更多的子类上进行有效性验证 。 这里子类溢出并不是唯一的问题 。想一想那些重复的代码,你需要重新设计你的整个类层次 。有没有更好的方法!确实,装饰器模式是避免这种情况的好方法 。 装饰器模式结构上类似与代理模式 。一个装饰器对象保留有对对象的引用,而且忠实的重新建立被装饰对象的公共接口 。装饰器也可以增加方法,扩展被装饰对象的接口,任意重载方法,甚至可以在脚本执行期间有条件的重载方法 。 为了探究装饰器模式,让我们以前面讨论过的表单组件库为例,并且用装饰器模式而不是继承,实现“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 /** * 装饰器模式的组成: * 抽象组件角色(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> "; } ?> <input type=”submit” value=”Submit”> </form> 现在,你已经拥有了个提交给它自身并且能保持posted数据的表单处理(form handler) 类 。 <?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> "; } ?> </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数组(Array)操作技巧大全》、《PHP基本语法入门教程》、《PHP运算与运算符用法总结》、《php字符串(string)用法总结》、《php+mysql数据库操作入门教程》及《php常见数据库操作技巧汇总》 希望本文所述对大家PHP程序设计有所帮助 。 |