案例
黑枣芭比公司是主营芭比娃娃游戏的公司,主要生产芭比娃娃,信息记录了一个芭比娃娃装备及美丽星级指数,可以看到,一个小眼睛扁鼻子阔嘴的芭比娃娃值0个指数。
<实现代码>
class Barbie
{
protected $eye = 'small';
protected $nose = 'flat';
protected $mouth = 'wide';
public $star = 0;
public function getInfo()
{
echo "Barbie Info\n";
echo " eye:" . $this->eye."\n";
echo " nose:" . $this->nose . "\n";
echo " mouth:" . $this->mouth . "\n";
}
public function setStar()
{
if ($this->eye == 'big') $this->star++;
if ($this->nose == 'stand') $this->star++;
if ($this->mouth == 'small') $this->star++;
}
public function getStar()
{
echo " Star:" . $this->star . "\n";
}
}
$ba = new Barbie();
$ba->getInfo();
$ba->getStar();
<系统要升级>
芭比娃娃可不是一成不变的,随着干爹之类的盛行,芭比犹如白富美一样不仅仅从眼睛鼻子嘴上下工夫,皮肤白加2星,腿长加3星,手细加1星,脖子细加1星。。。
<代码重构>
class Barbie
{
protected $eye = 'big';
protected $nose = 'flat';
protected $mouth = 'wide';
protected $skin = 'black';
protected $leg = 'short';
protected $hand = 'rough';
protected $neck = 'short';
public $star = 0;
public function getInfo()
{
echo "Barbie Info\n";
echo " eye:" . $this->eye . "\n";
echo " nose:" . $this->nose . "\n";
echo " mouth:" . $this->mouth . "\n";
}
public function setStar()
{
if ($this->eye == 'big') $this->star++;
if ($this->nose == 'stand') $this->star++;
if ($this->mouth == 'small') $this->star++;
if ($this->skin == 'white') $this->star += 2;
//省略中。。。
}
public function getStar(){
echo " Star:" . $this->star . "\n";
}
}
$ba = new Barbie();
$ba->getInfo();
$ba->getStar();
<需求升级变更>
芭比眼睛鼻子嘴皮肤腿手各分为18种类型,每个类型都有不同的星级,如一个打扮了大鼻子芭比娃娃可要扣2个星级。
如果这样下去,Barbie类可能超过5000行代码,而且这个庞大的类也很难维护更新。
程序猿于是已经崩溃
分析OOA:
为什么Barbie类代码会越写越多,因为它违背我们的设计原则:类应该对扩展开放,对修改关闭。我们的目标是允许类容易扩展。在不修改现有代码的基础上,就可以搭配新的行为。这样设计才可以接受新的功能来应对改变的需求
设计OOD:
<UML>
<说明>
1.Barbie为被装饰者。
2.NoseDecorator、MouthDecorator等类为装饰者,继承了Barbie并包含了Barbie的一个私有实例个两个公共方法。
3.Decorator类的构造方法,接收一个Barbie类型并将其存储在内部。
编程 OOP:
<代码>
class Barbie{
public function getInfo(){
echo "Barbie Info\n";
}
public function getStar(){
return 0;
}
}
class EyeDecorator extends Barbie {
private $barbie;
public function __construct(Barbie $barbie){
$this->barbie = $barbie;
}
public function getInfo(){
$this->barbie->getInfo();
echo " Eye:big\n";
}
public function getStar(){
return $this->barbie->getStar() + 2;
}
}
class NoseDecorator extends Barbie
{
private $barbie;
public function __construct(Barbie $barbie)
{
$this->barbie = $barbie;
}
public function getInfo()
{
$this->barbie->getInfo();
echo " Nose:small\n";
}
public function getStar()
{
return $this->barbie->getStar() + 1;
}
}
class MouthDecorator extends Barbie
{
private $barbie;
public function __construct(Barbie $barbie)
{
$this->barbie = $barbie;
}
public function getInfo()
{
$this->barbie->getInfo();
echo " Mouth:small\n";
}
public function getStar()
{
return $this->barbie->getStar() + 2;
}
}
class PlushskinDecorator extends Barbie
{
private $barbie;
public function __construct(Barbie $barbie)
{
$this->barbie = $barbie;
}
public function getInfo()
{
$this->barbie->getInfo();
echo " skin:Plush\n";
}
public function getStar()
{
return $this->barbie->getStar() - 10;
}
}
测试用例Test Case:
<代码>
class testDriver
{
public function run()
{
//创建一个高鼻梁大眼睛小嘴巴的芭比
$ba = new MouthDecorator(new EyeDecorator(new NoseDecorator(new Barbie())));
$ba->getInfo();
echo $ba->getStar();
//再给这个芭比加些毛
$ba = new PlushskinDecorator($ba);
$ba->getInfo();
echo $ba->getStar();
}
}
$test = new testDriver();
$test->run();
<输出>
小结:
1. 装饰者和被装饰对象有相同的超类型。
2. 可以用一个或多个装饰者包装一个对象。
3. 装饰者可以在所委托被装饰者的行为之前或之后,加上自己的行为,以达到特定的目的。
4. 对象可以在任何时候被装饰,所以可以在运行时动态的,不限量的用你喜欢的装饰者来装饰对象。
5. 装饰模式中使用继承的关键是想达到装饰者和被装饰对象的类型匹配,而不是获得其行为。
6. 装饰者一般对组件的客户是透明的,除非客户程序依赖于组件的具体类型。在实际项目中可以根据需要为装饰者添加新的行为,做到“半透明”装饰者。
7. 适配器模式的用意是改变对象的接口而不一定改变对象的性能,而装饰模式的用意是保持接口并增加对象的职责。
********************************************
* 作者:叶文涛
* 标题:Php设计模式之装饰者模式
* 时间:2012-5-15
* 参考:
*《PHP设计模式》(美)Aaron Saray著
*《PHP高级程序设计模式、框架与测试》(加)Kevin McArthur著
*《Head First设计模式》Eric Freeman等著
******************转载请注明来源 ***************