设计模式:装饰器(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 = ...
随机推荐
- c# 短链接生成
public static string GetShortUrl(string url) { //可以自定义生成MD5加密字符传前的混合KEY string key = DateTime.Now.To ...
- JMeter元件的作用域与执行顺序
元件的作用域 先来讨论一下元件有作用域.<JMeter基础元件介绍>一节中,我们介绍了8类可被执行的元件(测试计划与线程组不属于元件),这些元件中,取样器 是典型的不与其它元件发生交互作用 ...
- how to be an efficient man
"This Monday I was invited to do a presentation on this Friday, and today is Friday. I am going ...
- ExtJs6自定义scss解决actionColum中iconCls图标不能调样式的问题
问题:图标样式不对,icon(本地图片)是对的,iconCls(引用的)样式不对 查ExtJs6的API里面说,可以用style添加样式,然而并没有作用 最后在该文件树下建立scss,最好和view文 ...
- 记一次java程序内存溢出问题
一个自然语言处理程序,在封装为web-service后,部署到线上运行. 但最近出现了内存溢出的情况,频繁的out of memory. 先盲目尝试在启动脚本中增加-XX:-UseGCOverhead ...
- mysql主从复制测试
mysql主从复制测试: 1. 配置主服务器:在主库上面添加复制账号GRANT REPLICATION SLAVE on *.* to 'mark'@'%' identified by 'mark' ...
- 简述Spring及配置
简述Spring及配置 Spring最主要的思想就是IoC(Inversionof Control,控制反转),或者成为DI(Dependency Injection,依赖注入) 一.springMV ...
- apache和tomcat搭建集群
最近在学习简单的apache服务器和两个tomcat一起搭建集群,这里简单记录一下 1.准备工作 ①搭建一个可以运行的web项目 用maven搭建springmvc项目 ,只要将这里面的web.xml ...
- js原生带缩略图的图片切换效果
js原生带缩略图的图片切换效果 本例中用到的 moveElement(elementID,final_x,final_y,interval)是来自<JavaScript DOM编程艺术(中文第二 ...
- java中如何使用BigDecimal使得Double类型保留两位有效数字
一.场景:从数据表中读出Decimal类型的数据直接塞给Double类型的对象时,并不会有什么异常. 如果要再此基础上计算,就会发生异常. 比如:读出数据为0.0092,将其乘以100,则变成了0.9 ...