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

2020-05-02 14:01:11于丽

        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;
 }
 
}
?>

								 
			 
相关文章 大家在看