Symfony2中的设计模式——装饰者模式
装饰者模式的定义
文章链接:http://www.hcoding.com/?p=101
在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。
装饰者模式把每个要装饰的功能放在单独的类中,并让这个类包装它要装饰的对象,因此,当需要执行特殊行为时,客户端代码就可以在运行的时候根据需要有选择地、按顺序地使用装饰功能包装对象了。

图1
使用场景
设想一下,如果我们需要创建一个在不同场合有不同着装的学生,例如:在学校学生需要穿上校服,在舞会学生需要穿上正装,在家学生可以裸装(有点变态),当然,还可以学习超人把底裤穿在外面。这时候问题来了,难道我们要为每种场合编写一个不同穿着的学生类吗?如果我们的童鞋想要一个穿着校服裤子、正装上衣外露的底裤怎么办?StudentWithSchoolUniform、StudentWithFormalWear、StudentWithNaked、StudentWithSchoolUniformAndOutSideUnderWear..................绵绵无尽的类~~~累!是的,如果这样就造成类爆炸了,需求增加,类就不断的增加,整个系统的维护难度可想而知。
所以这时候,装饰者模式就可以发挥它的作用了,底裤、正装、校服、鞋子、眼镜等等都是具体的装饰者,学生是具体的被装饰的对象,被装饰的对象和装饰者的抽象类都继承者同一个父类。为学生穿上不同的服装,其实就是使用装饰者类(服装)包裹被装饰者类(学生),形象的说这是一个穿衣的过程。
类和接口
- Component(被装饰对象基类,对应例子的Person类)
- ConcreteComponent(具体被装饰对象,对应例子的Student类)
- Decorator(装饰者基类,对应例子的Costume)
- ContreteDecorator(具体的装饰者类,对应例子的Pants、Shirt等)
例子

图2
Person.php
<?php /**
* Person.php
* 被装饰基类
**/
abstract class Person{ public abstract function show(); }
Student.php
<?php /**
* Student.php
* 具体被装饰对象
**/
class Student extends Person{ private $name; public function __construct($name){
$this->name = $name;
} public function show(){
echo '我是学生',$this->name;
}
}
Costume.php
<?php /**
* Costume.php
* 装饰者基类
**/
abstract class Costume extends Person{ }
Shirt.php
<?php /**
* Shirt.php
* 具体的装饰者类
**/
class Shirt extends Costume{ private $person; public function __construct(Person $person){ $this->person = $person; } public function show(){ echo $this->person->show(),',穿着衬衫';
} }
Pants.php
<?php /**
* Pants.php
**/
class Pants extends Costume{ private $person; public function __construct(Person $person){ $this->person = $person; } public function show(){ echo $this->person->show(),',穿着裤子';
} }
Glasses.php
<?php /**
* Glasses.php
**/
class Glasses extends Costume{ private $person; public function __construct(Person $person){ $this->person = $person; } public function show(){ echo $this->person->show(),',带着眼镜';
} }
UnderWear.php
<?php /**
* UnderWear.php
**/
class UnderWear extends Costume{ private $person; public function __construct(Person $person){ $this->person = $person; } public function show(){ echo $this->person->show(),',穿着DK';
} }
Client.php
<?php
require_once 'Person.php';
require_once 'Costume.php';
require_once 'Student.php';
require_once 'UnderWear.php';
require_once 'Shirt.php';
require_once 'Pants.php';
require_once 'Glasses.php';
// Student继承Person
$jc = new Student('JC');
$jc->show(); // 我是学生JC
echo '<br>';
// 用UnderWear类装饰Person
$underwear = new UnderWear($jc);
$underwear->show(); // 我是学生JC,穿着DK
echo '<br>';
// 再用Pants类装饰Person
$pants = new Pants($underwear);
$pants->show(); // 我是学生JC,穿着DK,穿着裤子
echo '<br>';
// 再用Shirt类装饰Person
$shirt = new Shirt($pants);
$shirt->show(); // 我是学生JC,穿着DK,穿着裤子,穿着衬衫
echo '<br>';
// 再用Glasses类装饰Person
$glasses = new Glasses($shirt);
$glasses->show(); // 我是学生JC,穿着DK,穿着裤子,穿着衬衫,带着眼镜
echo '<br>';

图3 输出结果截图
Symfony2 EventDispatch 组件对装饰者模式的应用

图4 Symfony2 EventDispatch组件使用装饰模式

图5 Framework配置EventDispatcher
- Symfony\Component\EventDispatcher\EventDispatcherInterface 是被装饰的接口
- Symfony\Component\EventDispatcher\EventDispatcher 和 Symfony\Component\EventDispatcher\ContainerAwareEventDispatcher 是被装饰的具体对象
- Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcherInterface 装饰者接口
- Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcher 装饰者基类
- Symfony\Component\HttpKernel\Debug\TraceableEventDispatcher 具体的装饰者对象
具体装饰者对象Symfony\Component\HttpKernel\Debug\TraceableEventDispatcher::dispatch()方法,核心依旧是调用被装饰的具体对象Symfony\Component\EventDispatcher\EventDispatcher::dispatch()方法进行工作,但是装饰者对象Symfony\Component\HttpKernel\Debug\TraceableEventDispatcher::dispatch()方法添加了相应的功能,例如在调用Symfony\Component\EventDispatcher\EventDispatcher::dispatch()方法前后分别调用了preProcess()、preDispatch()和postDispatch()、postProcess():
/**
* {@inheritdoc}
*/
public function dispatch($eventName, Event $event = null)
{
if (null === $event) {
$event = new Event();
} // 装饰者对象增加的功能
$this->preProcess($eventName);
$this->preDispatch($eventName, $event); $e = $this->stopwatch->start($eventName, 'section'); // 核心依旧是调用被装饰的具体对象Symfony\Component\EventDispatcher\EventDispatcher::dispatch()方法
$this->dispatcher->dispatch($eventName, $event); if ($e->isStarted()) {
$e->stop();
} // 装饰者对象增加的功能
$this->postDispatch($eventName, $event);
$this->postProcess($eventName); return $event;
}
优点
- 通过组合而非继承的方式,实现了动态扩展对象的功能的能力。
- 有效避免了使用继承的方式扩展对象功能而带来的灵活性差,子类无限制扩张的问题。
- 充分利用了继承和组合的长处和短处,在灵活性和扩展性之间找到完美的平衡点。
- 装饰者和被装饰者之间虽然都是同一类型,但是它们彼此是完全独立并可以各自独立任意改变的。
- 遵守大部分GRASP原则和常用设计原则,高内聚、低偶合。
缺点
- 装饰链不能过长,否则会影响效率。
- 只在必要的时候使用装饰者模式,否则会提高程序的复杂性,增加系统维护难度。
- 装饰者对象和被装饰者对象都继承Component,如果Component内部发生变化,所有的子类都要改变。
Symfony2中的设计模式——装饰者模式的更多相关文章
- [Head First设计模式]山西面馆中的设计模式——装饰者模式
引言 在山西面馆吃鸡蛋面的时候突然想起装饰者这个模式,觉得面馆这个场景跟书中的星巴兹咖啡的场景很像,边吃边思考装饰者模式.这里也就依葫芦画瓢,换汤不换药的用装饰者模式来模拟一碗鸡蛋面是怎么出来的吧.吃 ...
- Java设计模式——装饰者模式
JAVA 设计模式 装饰者模式 用途 装饰者模式 (Decorator) 动态地给一个对象添加一些额外的职责.就增加功能来说,Decorator 模式相比生成子类更为灵活. 装饰者模式是一种结构式模式 ...
- JAVA设计模式--装饰器模式
装饰器模式 装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构.这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装. 这种模式创建了一个装饰 ...
- [转载]Java中继承、装饰者模式和代理模式的区别
[转载]Java中继承.装饰者模式和代理模式的区别 这是我在学Java Web时穿插学习Java设计模式的笔记 我就不转载原文了,直接指路好了: 装饰者模式和继承的区别: https://blog.c ...
- 从源码角度理解Java设计模式——装饰者模式
一.饰器者模式介绍 装饰者模式定义:在不改变原有对象的基础上附加功能,相比生成子类更灵活. 适用场景:动态的给一个对象添加或者撤销功能. 优点:可以不改变原有对象的情况下动态扩展功能,可以使扩展的多个 ...
- 【设计模式】Java设计模式 - 装饰者模式
Java设计模式 - 装饰者模式 不断学习才是王道 继续踏上学习之路,学之分享笔记 总有一天我也能像各位大佬一样 原创作品,更多关注我CSDN: 一个有梦有戏的人 准备将博客园.CSDN一起记录分享自 ...
- C#设计模式-装饰者模式
在软件开发中,我们经常想要对一类对象添加不同的功能,例如要给手机添加贴膜,手机挂件,手机外壳等,如果此时利用继承来实现的话,就需要定义无数的类,如StickerPhone(贴膜是手机类).Access ...
- 设计模式--装饰者模式(io流中使用的模式)
重点: 1.动态扩展对象,替换之前需要继承才能实现的功能. 2.具体工作的,仍然是被包装的对象,(有点锦上添花的意思,顾名思义仅仅起到装饰的作用,主体不变). 对比继承: 1.减少类的数量. 如果使用 ...
- JAVA 设计模式 装饰者模式
用途 装饰者模式 (Decorator) 动态地给一个对象添加一些额外的职责.就增加功能来说,Decorator 模式相比生成子类更为灵活. 装饰者模式是一种结构式模式. 结构
随机推荐
- favicon支持的图片格式
为网站设置favicon有两种方式: 1.网站根目录下放置名为favicon.ico的图片,浏览器就会自动获取: 2.在页面中通过<link rel="shortcut icon&qu ...
- QT下int与QByteArray的转换
int转QByteArray QByteArray intToByte(int i) { QByteArray abyte0; abyte0.resize(4); abyte0[0] = (uchar ...
- bzoj2244[SDOI2011]拦截导弹
http://www.lydsy.com/JudgeOnline/problem.php?id=2244 第$i$个导弹看成一个三元组$(i,h_i,v_i)$ 其实就是最长上升子序列的问题. 我们分 ...
- [认知]ClassLoader 认知一二三
曾经深入了解过ClassLoader,但一直没有时间去总结,现在以参考+自我认识的方式总结一下ClassLoader的相关内容,不能保证100%全,但目前对于一个毛头孩子来说,enough! 首先就要 ...
- cf500D New Year Santa Network
D. New Year Santa Network time limit per test 2 seconds memory limit per test 256 megabytes input st ...
- Android:ImageView应用之图片浏览器
ImageView控件实现的简单图片浏览器 一.纯显示图片: 引言: 读者在做这个东西的时候,需要自己把图片在源程序中导入. 读者要注意:所有导入的图片之前,图片的命名只可以是小写英文和数字. 效果图 ...
- Java Hibernate 之 Session 状态
Session接口是Hibernate向程序提供操纵数据库的最主要接口,是单线程对象,它提供了基本的保存.更新.删除和查询方法.它有一个缓存,保存了持久化对象,当清理缓存时,按照这些持久化对象同步更新 ...
- Hadoop2.4.1 使用MapReduce简单的数据清洗
package com.bank.service; import java.io.IOException;import java.text.ParseException;import java.tex ...
- zookeeper[2] zookeeper原理(转)
转自:http://cailin.iteye.com/blog/2014486 ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,它包含一个简单的原语集,分布式应用程序可以基于它实现 ...
- C++编程规范之19:总是初始化变量
摘要: 一切从白纸开始,未初始化的变量是C和C++程序中错误的常见来源.养成在使用内存之前先清除的习惯,可以避免这种错误,在定义变量的时候就将其初始化. 按照C和C++相同的低层高效率传统,通常并不要 ...