设计模式:装饰器(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 = ...
随机推荐
- 【转】selenium自动化测试用例需要关注的几点(一)
自动化测试设计简介 注:参看文章地址 我们在本章提供的信息,对自动化测试领域的新人和经验丰富的老手都是有用的.本篇中描述最常见的自动化测试类型, 还描述了可以增强您的自动化测试套件可维护性和扩展性的 ...
- java.utils.UUID类介绍
1 UUID介绍 UUID (Universally Unique Identifier)缩写,即通用唯一识别码,也是被开源软件基金会 (Open Software Foundation, OSF) ...
- EntityFrameWork Code First 多对多关系处理
场景2: 一个文章类别(Category)下含有多篇文章(Article),而文章也可能对应多个类别 Article和Category的代码更改如下: /// <summary> /// ...
- TOJ 1836 Play on Words
描述 Some of the secret doors contain a very interesting word puzzle. The team of archaeologists has t ...
- 九度oj 1031 xxx定律 2009年浙江大学计算机及软件工程研究生机试真题
题目1031:xxx定律 时间限制:1 秒 内存限制:32 兆 特殊判题:否 提交:5153 解决:3298 题目描述: 对于一个数n,如果是偶数,就把n砍掉一半:如果是奇数,把n变成 3*n ...
- UIBezierPath的使用方法
UIBezierPath的使用方式: 一,直接添加轨迹,然后stroke或者fill UIColor *blue =[UIColor blueColor]; [blue set]; UIBezierP ...
- Hbuilder编辑App时,ajax跨域访问失败问题
今天试着用Hbuilder写app的前段显示页面,在第一步时就被打住了,ajax异步调用服务器的登录接口时,报错, 显示这样的错误 XMLHttpRequest cannot loadhttp://w ...
- JavaScript对象中的this属性
this属性表示当前对象,如果在全局作用范围内使用this,则指代当前页面对象window: 如果在函数中使用this,则this指代什么是根据运行时此函数在什么对象上被调用. 我们还可以使用appl ...
- rabbit的fanout扇形交换机
rabbit引入交换机概念. 交换机与生产者绑定. 队列与消费者绑定. 队列又与交换机绑定. 扇形交换机是 fanout类型的. 类似于其他消息中间件的 topic.一对多(生产者推送消息到指定交换 ...
- springboot项目部署到独立tomcat的爬坑集锦
目录 集锦一:普通的springboot项目直接部署jar包 集锦二:springboot项目不能直接打war包部署 集锦三:因为tomcat版本问题导致的lombok插件报错:Invalid byt ...