设计模式:装饰器(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)模式的更多相关文章

  1. Head First 设计模式 —— 03. 装饰器 (Decorator) 模式

    思考题 有如下类设计: 如果牛奶的价钱上扬,怎么办?新增一种焦糖调料风味时,怎么办? 造成这种维护上的困难,违反了我们之前提过的哪种设计原则? P82 取出并封装变化的部分,让其他部分不收影响 多用组 ...

  2. 装饰器(Decorator)模式

    public interface IDoThings { public void doSomeThing(); } public class DoThings implements IDoThings ...

  3. JAVA设计模式--装饰器模式

    装饰器模式 装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构.这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装. 这种模式创建了一个装饰 ...

  4. python语法32[装饰器decorator](转)

    一 装饰器decorator decorator设计模式允许动态地对现有的对象或函数包装以至于修改现有的职责和行为,简单地讲用来动态地扩展现有的功能.其实也就是其他语言中的AOP的概念,将对象或函数的 ...

  5. python描述符(descriptor)、属性(property)、函数(类)装饰器(decorator )原理实例详解

     1.前言 Python的描述符是接触到Python核心编程中一个比较难以理解的内容,自己在学习的过程中也遇到过很多的疑惑,通过google和阅读源码,现将自己的理解和心得记录下来,也为正在为了该问题 ...

  6. python 装饰器(decorator)

    装饰器(decorator) 作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! 装饰器(decorator)是一种高级Python语 ...

  7. Python的程序结构[8] -> 装饰器/Decorator -> 装饰器浅析

    装饰器 / Decorator 目录 关于闭包 装饰器的本质 语法糖 装饰器传入参数 1 关于闭包 / About Closure 装饰器其本质是一个闭包函数,为此首先理解闭包的含义. 闭包(Clos ...

  8. Python_高阶函数、装饰器(decorator)

    一.变量: Python支持多种数据类型,在计算机内部,可以把任何数据都看成一个“对象”,而变量就是在程序中用来指向这些数据对象的,对变量赋值就是把数据和变量给关联起来. 对变量赋值x = y是把变量 ...

  9. python 语法之 装饰器decorator

    装饰器 decorator 或者称为包装器,是对函数的一种包装. 它能使函数的功能得到扩充,而同时不用修改函数本身的代码. 它能够增加函数执行前.执行后的行为,而不需对调用函数的代码做任何改变. 下面 ...

  10. python函数编程-装饰器decorator

    函数是个对象,并且可以赋值给一个变量,通过变量也能调用该函数: >>> def now(): ... print('2017-12-28') ... >>> l = ...

随机推荐

  1. robots 小记

    简介 网站所有者使用/robots.txt文件向网站机器人提供有关其网站的说明;这称为 Robots Exclusion Protocol.它的工作原理是这样的:robot 想要访问一个网站URL,比 ...

  2. 快速掌握用python写并行程序

    目录 一.大数据时代的现状 二.面对挑战的方法 2.1 并行计算 2.2 改用GPU处理计算密集型程序 3.3 分布式计算 三.用python写并行程序 3.1 进程与线程 3.2 全局解释器锁GIL ...

  3. vue打包后运行在本地/非服务器端环境的访问路径

    vue打包前的配置: 项目目录下--> config文件夹---> index.js: build:  { assetsPublickPath:  './',   // 设置成相对路径   ...

  4. Java入门系列-22-IO流

    File类的使用 Java程序如何访问文件?通过 java.io.File 类 使用File类需要先创建文件对象 File file=new File(String pathname);,创建时在构造 ...

  5. 线上服务器PHP版本编译安装升级全记录

    1.将原来的PHP重命名一下 cd /usr/local/bin/ mv php php.2.9 2.安装依赖 yum install gcc gcc-c++ libxml2 libxml2-deve ...

  6. C#基础笔记 转自wojiushigelg

    笔记如下: 概念:.net与c# .net/dontnet:一般指.net framework框架,一种平台,一种技术 c#(charp):一种编程语言,可以开发基于.net的应用. *java既是一 ...

  7. 快速清除SQL2008日志文件

    USE [master] --把数据库调整为简单模式 GO ALTER DATABASE krisvision SET RECOVERY SIMPLE WITH NO_WAIT GO ALTER DA ...

  8. redis操作基本命令

    Redis—— Remote Dictionary Server,它是一个开源的使用ANSI C语言编写.支持网络.可基于内存亦可持久化的日志型.Key-Value数据库,并提供多种语言的API,我们 ...

  9. 2017年11月26日 C#流&&窗体对话框

    C#流 在顶端加入using System.IO就可以使用流 StreamReder a = new StreamReder();//读取 前面什么都可以 = sr.ReadToEnd();//用法 ...

  10. 关于css伪类,伪元素详解总结

    伪类 伪类就是一种虚构的状态或者说是一个具有特殊属性的元素可以使用CSS进行样式修饰.常见的几种伪类是: :link , :visited , :hover , :active , :first-ch ...