首页 > 代码库 > Symfony2中的设计模式——装饰者模式
Symfony2中的设计模式——装饰者模式
装饰者模式的定义
在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。
装饰者模式把每个要装饰的功能放在单独的类中,并让这个类包装它要装饰的对象,因此,当需要执行特殊行为时,客户端代码就可以在运行的时候根据需要有选择地、按顺序地使用装饰功能包装对象了。
图1
使用场景
设想一下,如果我们需要创建一个在不同场合有不同着装的学生,例如:在学校学生需要穿上校服,在舞会学生需要穿上正装,在家学生可以裸装(有点变态),当然,还可以学习超人把底裤穿在外面。这时候问题来了,难道我们要为每种场合编写一个不同穿着的学生类吗?如果我们的童鞋想要一个穿着校服裤子、正装上衣外露的底裤怎么办?StudentWithSchoolUniform、StudentWithFormalWear、StudentWithNaked、StudentWithSchoolUniformAndOutSideUnderWear..................绵绵无尽的类~~~累!是的,如果这样就造成类爆炸了,需求增加,类就不断的增加,整个系统的维护难度可想而知。
所以这时候,装饰者模式就可以发挥它的作用了,底裤、正装、校服、鞋子、眼镜等等都是具体的装饰者,学生是具体的被装饰的对象,被装饰的对象和装饰者的抽象类都继承者同一个父类。为学生穿上不同的服装,其实就是使用装饰者类(服装)包裹被装饰者类(学生),形象的说这是一个穿衣的过程。
类和接口
- Component(被装饰对象基类,对应例子的Person类)
- ConcreteComponent(具体被装饰对象,对应例子的Student类)
- Decorator(装饰者基类,对应例子的Costume)
- ContreteDecorator(具体的装饰者类,对应例子的Pants、Shirt等)
例子
图2
Person.php
1 <?php 2 3 /** 4 * Person.php 5 * 被装饰基类 6 **/ 7 abstract class Person{ 8 9 public abstract function show();10 11 }
Student.php
1 <?php 2 3 /** 4 * Student.php 5 * 具体被装饰对象 6 **/ 7 class Student extends Person{ 8 9 private $name;10 11 public function __construct($name){12 $this->name = $name;13 }14 15 public function show(){16 echo ‘我是学生‘,$this->name;17 }18 }
Costume.php
1 <?php 2 3 /** 4 * Costume.php 5 * 装饰者基类 6 **/ 7 abstract class Costume extends Person{ 8 9 10 }
Shirt.php
1 <?php 2 3 /** 4 * Shirt.php 5 * 具体的装饰者类 6 **/ 7 class Shirt extends Costume{ 8 9 private $person;10 11 public function __construct(Person $person){12 13 $this->person = $person;14 15 }16 17 public function show(){18 19 echo $this->person->show(),‘,穿着衬衫‘;20 }21 22 }
Pants.php
1 <?php 2 3 /** 4 * Pants.php 5 **/ 6 class Pants extends Costume{ 7 8 private $person; 9 10 public function __construct(Person $person){11 12 $this->person = $person;13 14 }15 16 public function show(){17 18 echo $this->person->show(),‘,穿着裤子‘;19 }20 21 }
Glasses.php
1 <?php 2 3 /** 4 * Glasses.php 5 **/ 6 class Glasses extends Costume{ 7 8 private $person; 9 10 public function __construct(Person $person){11 12 $this->person = $person;13 14 }15 16 public function show(){17 18 echo $this->person->show(),‘,带着眼镜‘;19 }20 21 }
UnderWear.php
1 <?php 2 3 /** 4 * UnderWear.php 5 **/ 6 class UnderWear extends Costume{ 7 8 private $person; 9 10 public function __construct(Person $person){11 12 $this->person = $person;13 14 }15 16 public function show(){17 18 echo $this->person->show(),‘,穿着DK‘;19 }20 21 }
Client.php
1 <?php 2 3 require_once ‘Person.php‘; 4 require_once ‘Costume.php‘; 5 require_once ‘Student.php‘; 6 require_once ‘UnderWear.php‘; 7 require_once ‘Shirt.php‘; 8 require_once ‘Pants.php‘; 9 require_once ‘Glasses.php‘;10 11 // Student继承Person12 $jc = new Student(‘JC‘);13 $jc->show(); // 我是学生JC14 echo ‘<br>‘;15 16 // 用UnderWear类装饰Person17 $underwear = new UnderWear($jc);18 $underwear->show(); // 我是学生JC,穿着DK19 echo ‘<br>‘;20 21 // 再用Pants类装饰Person22 $pants = new Pants($underwear);23 $pants->show(); // 我是学生JC,穿着DK,穿着裤子24 echo ‘<br>‘;25 26 // 再用Shirt类装饰Person27 $shirt = new Shirt($pants);28 $shirt->show(); // 我是学生JC,穿着DK,穿着裤子,穿着衬衫29 echo ‘<br>‘;30 31 // 再用Glasses类装饰Person32 $glasses = new Glasses($shirt);33 $glasses->show(); // 我是学生JC,穿着DK,穿着裤子,穿着衬衫,带着眼镜34 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():
1 /** 2 * {@inheritdoc} 3 */ 4 public function dispatch($eventName, Event $event = null) 5 { 6 if (null === $event) { 7 $event = new Event(); 8 } 9 10 // 装饰者对象增加的功能11 $this->preProcess($eventName);12 $this->preDispatch($eventName, $event);13 14 $e = $this->stopwatch->start($eventName, ‘section‘);15 16 // 核心依旧是调用被装饰的具体对象Symfony\Component\EventDispatcher\EventDispatcher::dispatch()方法17 $this->dispatcher->dispatch($eventName, $event);18 19 if ($e->isStarted()) {20 $e->stop();21 }22 23 // 装饰者对象增加的功能24 $this->postDispatch($eventName, $event);25 $this->postProcess($eventName);26 27 return $event;28 }
优点
- 通过组合而非继承的方式,实现了动态扩展对象的功能的能力。
- 有效避免了使用继承的方式扩展对象功能而带来的灵活性差,子类无限制扩张的问题。
- 充分利用了继承和组合的长处和短处,在灵活性和扩展性之间找到完美的平衡点。
- 装饰者和被装饰者之间虽然都是同一类型,但是它们彼此是完全独立并可以各自独立任意改变的。
- 遵守大部分GRASP原则和常用设计原则,高内聚、低偶合。
缺点
- 装饰链不能过长,否则会影响效率。
- 只在必要的时候使用装饰者模式,否则会提高程序的复杂性,增加系统维护难度。
- 装饰者对象和被装饰者对象都继承Component,如果Component内部发生变化,所有的子类都要改变。
Symfony2中的设计模式——装饰者模式