设计模式:装饰器(Decorator)模式
设计模式:装饰器(Decorator)模式
一、前言
装饰器模式也是一种非常重要的模式,在Java以及程序设计中占据着重要的地位。比如Java的数据流处理,我们可能看到数据流经过不同的类的包装和包裹,最终形成了我们需要的流,比如说从二进制到字节流再到字符流,这中间其实就是经过了装饰器的处理,在不改变原来的数据的基础上,加入属于自己的特征,就像是在一块蛋糕上加上一些水果等装饰品,这样输出的结果就不同了,我们将这种能产生类似于洋葱一样层层包裹的数据格式的设计模式成为装饰器模式。
那么为什么装饰器这样神奇呢,几乎可以让我们无穷尽的包裹自身,在计算机中凡是能够无穷尽的重复某一件事物,必然不能设定固定的数据,只能按照用户的随意设置来计算,那么递归的魅力就在此彰显出来了,通过递归可以实现这一点,那么如何递归呢,我们想到一层套一层的数据结构(链表、二叉树等),就必须在“自身”中包含“自身”;前一个“自身”可以是我们的子类,因为子类有着父类的所有特点,后一个“自身”就是具有最抽象资格的“自身”,正是因为足够抽象使得任何继承与这个抽象类的类都能通过里氏代换原则转换到这个抽象类。实现了递归,我们只需要停止条件就可以了,停止条件就是用户在Main中对原数据包裹的层数,肯定是有限的,因此停止是必然的。这样我们仿似看到一个原始数据,被一层层的进行包裹,加入新的内容,最终使用最顶层的输出方法将这个结果呈现给我们。同样的我们还可以反着看,当递归开始的时候,在最顶层等待下一层的数据,然后使用顶层的方式来封装,而下一层被启动执行到关键步骤时会等待下下一层的数据返回给自身,然后是用自己的方式来封装,就这样一直等待下去,直到最底层的数据(本来就有)得到之后,然后一步步的返回过来,在相应的层次进行相应的封装,最后得到了最终的数据。这就是装饰器模式,所有的类其实最终都是同源(一致性)的,有最终的祖先,如下图所示。

二、代码
上图中,StringDisplay是保存原始数据的,而Border中将父类的引用组合进入自身,形成了递归的必然条件,之后让子类(SideBorder和FullBorder)来使用这个引用,从而根据自身的实际情况(要怎样装饰)来进行包装,将原始的数据getRowText(rowID)进行包裹,最终通过同源祖先类的show()方法来现实,这里祖先类Display使用了面向抽象编程的模板方法,相信大家都能看出来。对比与上一个组合模式,我们可以看到上面的部分还是很相似的,但是在composite中,都实现了add()方法,通过容器来进行组织,并且使用委托机制来存储所有的有着共同父类的元素,在显示的时候使用了树的深度优先遍历。而在装饰器模式中,我们使用的递归从上到下,沿着display的指向一点点的走到了底部,并且最终返回了过来。遍历的方式有着相似之处也有着不同之处。这里要说明的是,建议大家在show()的地方打个断点,然后跟踪进去,一点点的看看我们的程序到底是怎么组织起来的,只有这样我们才能理解递归的含义,对装饰器有一个更深层次的认识。
Display 类:
package zyr.dp.decorator;
public abstract class Display {
public abstract int getColunmns();
public abstract int getRows();
public abstract String getTowText(int rowID);
public void show(){
for(int i=0;i<getRows();i++){
System.out.println(getTowText(i));
}
}
}
StringDisplay类:
package zyr.dp.decorator;
public class StringDisplay extends Display {
String name;
public StringDisplay(String name){
this.name=name;
}
public int getColunmns() {
return name.getBytes().length;
}
public int getRows() {
return 1;
}
public String getTowText(int rowID) {
if(rowID==0){
return name;
}else{
return null;
}
}
}
Border类:
package zyr.dp.decorator;
public abstract class Border extends Display {
protected Display display;
public Border(Display display){
this.display=display;
}
}
SideBorder类:
package zyr.dp.decorator;
public class SideBorder extends Border {
String ch;
protected SideBorder(Display display,String ch) {
super(display);
this.ch=ch;
}
public int getColunmns() {
return display.getColunmns()+2;
}
public int getRows() {
return display.getRows();
}
public String getTowText(int rowID) {
return ch+display.getTowText(rowID)+ch;
}
}
FullBorder类:
package zyr.dp.decorator;
public class FullBorder extends Border {
public FullBorder(Display display) {
super(display);
}
public int getColunmns() {
return display.getColunmns()+2;
}
public int getRows() {
return display.getRows()+2;
}
public String getTowText(int rowID) {
if(rowID==0){
return "+"+makeLine("-",display.getColunmns())+"+";
}else if(rowID==display.getRows()+1){
return "+"+makeLine("-",display.getColunmns())+"+";
}else{
return "|"+display.getTowText(rowID-1)+"|";
}
}
private String makeLine(String ch,int count){
StringBuffer sb=new StringBuffer();
for(int i=0;i<count;i++){
sb.append(ch);
}
return sb.toString();
}
}
Main类:
package zyr.dp.decorator;
public class Main {
public static void main(String[] args) {
Display d1=new StringDisplay("朱彦荣");
d1.show();
System.out.println("\n");
Display d2=new SideBorder(d1,"*");
d2.show();
System.out.println("\n");
Display d3=new FullBorder(d2);
d3.show();
System.out.println("\n");
Display d4=new SideBorder(
new FullBorder(
new FullBorder(
new SideBorder(
new FullBorder(
new StringDisplay("李山秀")
),
"#"
)
)
),
"*"
);
d4.show();
}
}
运行结果:

我们来跟踪一下d4.show()的执行过程:







多次跟踪,最终到了终点,然后一步步回溯过去。跟踪下一条语句:



。。。



。。。

。。。
就是这样不断地深入,得到结果之后,一步步返回,最后输出的。
三、总结
继承保证了父类和子类的一致性(有共同的方法),委托保证了使用委托的类和被委托对象的一致性。正如Border和Display有着一些相同的方法名称,以及一些委托处理方法。可以看到装饰模式中,保证了装饰边框与被装饰物体的一致性(有共同父类),使用了模板方法,这个方法几乎无处不在呀,同样使用了委托(组合),通过在原始数据上面一层层的包裹,最终得到了我们想要的输出,有着非常广泛的用处。
设计模式:装饰器(Decorator)模式的更多相关文章
- Head First 设计模式 —— 03. 装饰器 (Decorator) 模式
思考题 有如下类设计: 如果牛奶的价钱上扬,怎么办?新增一种焦糖调料风味时,怎么办? 造成这种维护上的困难,违反了我们之前提过的哪种设计原则? P82 取出并封装变化的部分,让其他部分不收影响 多用组 ...
- 装饰器(Decorator)模式
public interface IDoThings { public void doSomeThing(); } public class DoThings implements IDoThings ...
- JAVA设计模式--装饰器模式
装饰器模式 装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构.这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装. 这种模式创建了一个装饰 ...
- python语法32[装饰器decorator](转)
一 装饰器decorator decorator设计模式允许动态地对现有的对象或函数包装以至于修改现有的职责和行为,简单地讲用来动态地扩展现有的功能.其实也就是其他语言中的AOP的概念,将对象或函数的 ...
- python描述符(descriptor)、属性(property)、函数(类)装饰器(decorator )原理实例详解
1.前言 Python的描述符是接触到Python核心编程中一个比较难以理解的内容,自己在学习的过程中也遇到过很多的疑惑,通过google和阅读源码,现将自己的理解和心得记录下来,也为正在为了该问题 ...
- python 装饰器(decorator)
装饰器(decorator) 作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! 装饰器(decorator)是一种高级Python语 ...
- Python的程序结构[8] -> 装饰器/Decorator -> 装饰器浅析
装饰器 / Decorator 目录 关于闭包 装饰器的本质 语法糖 装饰器传入参数 1 关于闭包 / About Closure 装饰器其本质是一个闭包函数,为此首先理解闭包的含义. 闭包(Clos ...
- Python_高阶函数、装饰器(decorator)
一.变量: Python支持多种数据类型,在计算机内部,可以把任何数据都看成一个“对象”,而变量就是在程序中用来指向这些数据对象的,对变量赋值就是把数据和变量给关联起来. 对变量赋值x = y是把变量 ...
- python 语法之 装饰器decorator
装饰器 decorator 或者称为包装器,是对函数的一种包装. 它能使函数的功能得到扩充,而同时不用修改函数本身的代码. 它能够增加函数执行前.执行后的行为,而不需对调用函数的代码做任何改变. 下面 ...
- python函数编程-装饰器decorator
函数是个对象,并且可以赋值给一个变量,通过变量也能调用该函数: >>> def now(): ... print('2017-12-28') ... >>> l = ...
随机推荐
- IONIC 打包安卓apk详细过程
参照以下链接: https://blog.csdn.net/qq_20264891/article/details/79319408 当 cordova 项目安装的 android 平台版本 与 系统 ...
- 九度oj 1004 Median 2011年浙江大学计算机及软件工程研究生机试真题
题目1004:Median 时间限制:1 秒 内存限制:32 兆 特殊判题:否 提交:14162 解决:3887 题目描述: Given an increasing sequence S of N i ...
- list-iscroll5.2
简介 iScroll是一个高性能,资源占用少,无依赖,多平台的JavaScript滚动插件. 它可以在桌面,移动设备和智能电视平台上工作.它一直在大力优化性能和文件大小以便在新旧设备上提供最顺畅的体验 ...
- ExtJs6解决添加和修改Form共用一个form的隐藏域的id的取消传值
问题重现:修改不会有问题,id会绑定之前的grid,有具体数字 添加有问题,因为id是空,传的是绑定值的话会显示“类名-1”,从int类型变成了string类型,后台会出错 这是EduQuestion ...
- vue加载Element ui地址省市区插件-- element-china-area-data
1.安装 npm install element-china-area-data -S 2.使用(引入) import { provinceAndCityData, regionData, provi ...
- sqlserver 自增ID插入指定数据(转)
set identity_insert 表名 ON --允许对自增列Id插入指定数据 insert into table_name(Id,Name) values(1,'test') set iden ...
- PS基础,英语
PS基础(矢量图案的绘制) 水平参考线:新建背景(长宽一致,背景内容为透明)---设置水平参考线(水平垂直都要)---完成. 背景制作:设置前景色---用矩形选框工具绘制正方形选区(背景已被参考线平分 ...
- CentOS7 一键安装KMS服务【整理】
KMS,是 Key Management System 的缩写,也就是密钥管理系统.这里所说的 KMS,毋庸置疑就是用来激活 VOL 版本的 Windows 和 Office 的 KMS 啦.经常能在 ...
- Spring 基础入门(一)
本文代码部分来自于<spring in action>,本文讲的是使用!! Spring 是为了解决什么 一个框架的存在是为了解决某个问题的,那么Spring这个框架是为了解决什么问题呢? ...
- The Struts dispatcher cannot be found. This is usually caused by using Struts tags without the associated filter.
The Struts dispatcher cannot be found. This is usually caused by using Struts tags without the assoc ...