PHP设计模式之装饰者模式


Posted in PHP onFebruary 29, 2012

介绍
装饰者模式动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。

思维导图

PHP设计模式之装饰者模式

 

有这样一个项目,做一个餐厅订餐系统。起初的代码结构是这样的。前面有很多Beverage的继承类,现在遇到的问题是牛奶的价钱上涨了,那么所有相关的类,我们都要进行调整,比如Milk,SugarAndMilk类,这种类还有很多,我们需要逐个去修改类中的方法——开发人员每次都做这种事情,要疯了!所以我们要改变现有的结构。以下的图都是简图,实际的图,可没有这么简单。

 

PHP设计模式之装饰者模式

 

 设计问题:

1》类数量爆炸,有很多类,难以维护;
2》整个设计呆板;
3》基类加入的新功能无法使用于子类;
复用类方法的方式很多,比如继承,组合,委托。为什么老是习惯用继承呢?我看Zend Framework也有这种习惯!每次找对应方法,一直往上翻。——题外话!!!!
后来经过小组研究决定,我们决定把基础类抽出来,比如,我们把咖啡做成一个单独的类,其他的咖啡,比如牛奶咖啡,甜味咖啡,我们只对材料单独包装成一个类。
经过改良的设计:

PHP设计模式之装饰者模式

详解
1》对于饮品,我们直接继承Beverage类,直接把报价写进饮品类里面;
2》而对于一些需要添加调味品的特殊饮品,我们做累加操作。比如,我想要杯奶咖啡,则 总价=咖啡价+奶价
3》这样不同的饮料就很容易知道它的价格。
代码

<?php 
abstract class Beverage{ 
public $_name; 
abstract public function Cost(); 
} 
// 被装饰者类 
class Coffee extends Beverage{ 
public function __construct(){ 
$this->_name = 'Coffee'; 
} 
public function Cost(){ 
return 1.00; 
} 
} 
// 以下三个类是装饰者相关类 
class CondimentDecorator extends Beverage{ 
public function __construct(){ 
$this->_name = 'Condiment'; 
} 
public function Cost(){ 
return 0.1; 
} 
} 
class Milk extends CondimentDecorator{ 
public $_beverage; 
public function __construct($beverage){ 
$this->_name = 'Milk'; 
if($beverage instanceof Beverage){ 
$this->_beverage = $beverage; 
}else 
exit('Failure'); 
} 
public function Cost(){ 
return $this->_beverage->Cost() + 0.2; 
} 
} 
class Sugar extends CondimentDecorator{ 
public $_beverage; 
public function __construct($beverage){ 
$this->_name = 'Sugar'; 
if($beverage instanceof Beverage){ 
$this->_beverage = $beverage; 
}else{ 
exit('Failure'); 
} 
} 
public function Cost(){ 
return $this->_beverage->Cost() + 0.2; 
} 
} 
// Test Case 
//1.拿杯咖啡 
$coffee = new Coffee(); 
//2.加点牛奶 
$coffee = new Milk($coffee); 
//3.加点糖 
$coffee = new Sugar($coffee); 
printf("Coffee Total:%0.2f元\n",$coffee->Cost());

总结
1.装饰者(Milk)和被装饰者(Coffee)必须是一样的类型。目的是装饰者必须取代被装饰者。
2.添加行为:当装饰者和组件组合时,就是在加入新的行为。
题外话:
1.利用继承设计子类行为,是在编译时静态决定的,而且所有的子类都会继承到相同的行为。打个比方,老子想学点功夫,看你小子会太极拳,老子只需要继承你一下 ,老子也就会太极拳了——呵呵,这时老子就变成你儿子了,看来继承是要付出代价的。
2.组合,我们可以扩展对象的行为,在运行时动态地进行扩展。利用组合我们可以随时把我们当时设计超类时没有想到的方法加入到对象中,而不用改变现有的代码。打个比方,老子现在没有内力,吸功大法,把和尚,尼姑,道士的内力(行为对象)都吸过来,那在搏斗(运行时)中,老子可以随时都能使用不同的内力,但也不能胡乱吸内力,否则你就要走火入魔了!
3.类应该对扩展开放,对修改关闭。如果我们每个部分都用装饰者模式进行设计,那么对于整个框架来说有点浪费,而且你也加大了代码的难度。那什么时候使用这种模式呢?我们一般用于经常改变的地方。那我们又怎么知道哪些是经常改变的地方呢?这个就需要我们的经验和你对所处行业的了解。建议大家平时多看点例子。
4.装饰模式为设计注入弹性,但同时会在设计中加入大量的小类,这偶尔会导致别人不容易了解这种设计。
5.在使用装饰者模式的时候,对插入的的装饰者要特别小心。因为装饰者模式依赖某种特定的类型(Beverage)。
6.要想很好的使用装饰者模式,我们还要配合使用工厂模式和生成器模式,但今天只说装饰者模式。要想知道更多,请听下回分解。
参考文献:《head first 设计模式》
PHP 相关文章推荐
网友原创的PHP模板类代码
Sep 07 PHP
Php Cookie的一个使用注意点
Nov 08 PHP
备份mysql数据库的php代码(一个表一个文件)
May 28 PHP
解析ajax事件的调用顺序
Jun 17 PHP
php实现12306火车票余票查询和价格查询(12306火车票查询)
Jan 14 PHP
使用PHP生成二维码的两种方法(带logo图像)
Mar 14 PHP
教你如何解密 “ PHP 神盾解密工具 ”
Jun 20 PHP
PHP判断是否为空的几个函数对比
Apr 21 PHP
用PHP代码在网页上生成图片
Jul 01 PHP
启用Csrf后POST数据时出现的400错误
Jul 05 PHP
详解Laravel5.6 Passport实现Api接口认证
Jul 27 PHP
PHP命令行与定时任务
Apr 01 PHP
php preg_filter执行一个正则表达式搜索和替换
Feb 27 #PHP
mysql总结之explain
Feb 27 #PHP
php&amp;mysql 日期操作小记
Feb 27 #PHP
MySQL时间字段究竟使用INT还是DateTime的说明
Feb 27 #PHP
php explode函数实例代码
Feb 27 #PHP
PHP中获取文件扩展名的N种方法小结
Feb 27 #PHP
PHP中的正则表达式函数介绍
Feb 27 #PHP
You might like
php结合表单实现一些简单功能的例子
2011/06/04 PHP
javascript使用activex控件的代码
2011/01/27 Javascript
Extjs EditorGridPanel中ComboBox列的显示问题
2011/07/04 Javascript
浅析JS刷新框架中的其他页面 &amp;&amp; JS刷新窗口方法汇总
2013/07/08 Javascript
appendChild() 或 insertBefore()使用与区别介绍
2013/10/11 Javascript
基于jquery实现的图片在各种分辨率下未知的容器内上下左右居中
2014/05/11 Javascript
一个简单的全屏图片上下打开显示网页效果示例
2014/07/08 Javascript
js实现在网页上简单显示时间的方法
2015/03/02 Javascript
基于jquery实现图片上传本地预览功能
2016/01/08 Javascript
javascript实现平滑无缝滚动
2020/08/09 Javascript
JavaScript代码性能优化总结(推荐)
2016/05/16 Javascript
Backbone View 之间通信的三种方式
2016/08/09 Javascript
js select实现省市区联动选择
2020/04/17 Javascript
Vue.js每天必学之过渡与动画
2016/09/06 Javascript
js倒计时小实例(多次定时)
2016/12/08 Javascript
js仿百度音乐全选操作
2017/01/13 Javascript
通过js动态创建标签,并设置属性方法
2018/02/24 Javascript
深入理解Node module模块
2018/03/26 Javascript
vue2中引用及使用 better-scroll的方法详解
2018/11/15 Javascript
使用mixins实现elementUI表单全局验证的解决方法
2019/04/02 Javascript
Angular2使用SVG自定义图表(条形图、折线图)组件示例
2019/05/10 Javascript
Vue图片浏览组件v-viewer用法分析【支持旋转、缩放、翻转等操作】
2019/11/04 Javascript
JavaScript字符和ASCII实现互相转换
2020/06/03 Javascript
在vue中实现清除echarts上次保留的数据(亲测有效)
2020/09/09 Javascript
[49:15]DOTA2-DPC中国联赛 正赛 CDEC vs XG BO3 第二场 1月19日
2021/03/11 DOTA
python使用nntp读取新闻组内容的方法
2015/05/08 Python
Python 中 function(#) (X)格式 和 (#)在Python3.*中的注意事项
2018/11/30 Python
如何使用PyCharm将代码上传到GitHub上(图文详解)
2020/04/27 Python
自动化专业职业生涯规划书范文
2014/01/16 职场文书
技校毕业生个人学习的自我评价
2014/02/21 职场文书
大气污染防治方案
2014/05/19 职场文书
大学毕业生求职自荐书
2014/06/05 职场文书
银行求职信范文怎么写
2015/03/20 职场文书
怒海潜将观后感
2015/06/11 职场文书
退休欢送会致辞
2015/07/31 职场文书
PyTorch 实现L2正则化以及Dropout的操作
2021/05/27 Python