在阎宏博士的《JAVA与模式》一书中开头是这样描述装饰(Decorator)模式的:

  装饰模式又名包装(Wrapper)模式。装饰模式以对客户端透明的方式扩展对象的功能,是继承关系的一个替代方案。

  装饰模式的类图如下:

  

  涉及到的角色:

  抽象构件(Component)角色:给出一个抽象接口,以规范接收附加责任的对象。

  具体构件(ConcreteComponent)角色:定义一个接收附加责任的类。

  装饰(Decorator)角色:持有一个构件(Component)对象的实例,并定义一个与抽象构件接口一致的接口。

  具体装饰(ConcreteDecorator)角色:负责给构件对象“贴上”附加的责任。

  抽象构件角色:

 public interface Component {

     public void sampleOperation();

 }

  具体构件角色:

 public class ConcreteComponent implements Component {

     @Override
public void sampleOperation() {
// 写相关的业务代码
} }

  装饰角色:

 public class Decorator implements Component{
private Component component; public Decorator(Component component){
this.component = component;
} @Override
public void sampleOperation() {
// 委派给构件
component.sampleOperation();
} }

  具体装饰角色:

 public class ConcreteDecoratorA extends Decorator {

     public ConcreteDecoratorA(Component component) {
super(component);
} @Override
public void sampleOperation() {
     super.sampleOperation();
// 写相关的业务代码
}
}
 public class ConcreteDecoratorB extends Decorator {

     public ConcreteDecoratorB(Component component) {
super(component);
} @Override
public void sampleOperation() {
     super.sampleOperation();
// 写相关的业务代码
}
}

  齐天大圣:

  孙悟空有七十二变,他的每一种变化都给他带来一种附加的本领。他变成鱼儿时,就可以到水里游泳;他变成鸟儿时,就可以在天上飞行。

  本例中,Component角色由齐天大圣扮演,ConcreteComponent角色属于大圣本尊即猢狲本人。Decorator角色由大圣的七十二变扮演,ConcreteDecorator角色是鱼儿、鸟儿等72种具体变化。

  

  抽象构件角色齐天大圣接口:

 //大圣的尊号
public interface TheGreatestSage { public void move();
}

  具体构件角色大圣本尊猢狲类:

 public class Monkey implements TheGreatestSage {

     @Override
public void move() {
//代码
System.out.println("Monkey Move");
} }

  抽象装饰角色七十二变:

 public class Change implements TheGreatestSage {
private TheGreatestSage sage; public Change(TheGreatestSage sage){
this.sage = sage;
}
@Override
public void move() {
// 代码
sage.move();
} }

  具体装饰角色鱼儿:

 public class Fish extends Change {

     public Fish(TheGreatestSage sage) {
super(sage);
} @Override
public void move() {
// 代码
System.out.println("Fish Move");
}
}

  具体装饰角色鸟儿:

 public class Bird extends Change {

     public Bird(TheGreatestSage sage) {
super(sage);
} @Override
public void move() {
// 代码
System.out.println("Bird Move");
}
}

  客户端:

 public class Client {

     public static void main(String[] args) {
TheGreatestSage sage = new Monkey();
// 第一种写法
TheGreatestSage bird = new Bird(sage);
TheGreatestSage fish = new Fish(bird);
// 第二种写法
//TheGreatestSage fish = new Fish(new Bird(sage));
fish.move();
} }

  大圣本尊是ConcreteComponent类。鸟儿、鱼儿是装饰类,装饰的是大圣本尊即猢狲实例。把大圣从一只猢狲装饰成一只鸟儿(把鸟儿的功能加到猢狲上),然后又把鸟儿装饰成了一条鱼儿(把鱼儿的功能加到猢狲+鸟儿上,得到了猢狲+鸟儿+鱼儿)。

  

  

  装饰模式的简化

  如果只有一个ConcreteComponent类,可以去掉Component接口,把Decorator当成一个ConcreteComponent子类,见下图:

  

  如果只有一个ConcreteDecorator类或者只有两个ConcreteDecorator类,可以把Decorator和ConcreteDecorator合并成一个类,见下图:

  

  透明性的要求:

  装饰模式对客户端的透明性要求程序不声明一个ConcreteComponent类型的变量,而要声明一个Component类型的变量。

  用孙悟空的例子来说,必须把孙悟空的所有变化都当成孙悟空来对待。如果把孙悟空变成的鱼儿当成鱼儿,那就被孙悟空骗了。

  正确做法:

 TheGreatestSage sage = new Monkey();
TheGreatestSage bird = new Bird(sage);

  错误做法:

 Monkey sage = new Monkey();
Bird bird = new Bird(sage);

  半透明的装饰模式:

  装饰模式的目的是在不改变接口的前提下,增强类的性能。在增强性能时,需要创建新的公有方法。用孙悟空的例子来说:齐天大圣类没有飞行能力,而鸟儿有,所以鸟儿类里应该有一个新的fly方法。齐天大圣类没有游泳能力,而鱼儿有,所以鱼儿类里应该有一个新的swim方法。

  装饰模式和适配器模式都是包装模式(Wrapper Pattern),通过封装其他对象达到目的。理想的装饰模式在对被装饰对象进行功能增强时,要求具体构件角色、装饰角色的接口与抽象构件角色的接口一致。适配器模式会改变源对象的接口,与目标接口一致。装饰模式有透明和半透明两种,区别在于装饰角色的接口与抽象构件角色的接口是否一致。透明的装饰模式即理想的装饰模式要求具体构件角色、装饰角色的接口与抽象构件角色的接口一致。如果装饰角色的接口比抽象构件角色的接口宽,装饰角色成了适配器角色,称为半透明的装饰模式,见下图:

  

  适配器类的接口会比被装饰的目标类接口宽。

  因此,大多数的装饰模式的实现都是半透明的。也就是说,允许装饰模式在具体装饰类里增加新的方法。用孙悟空的例子来说:

 TheGreatestSage sage = new Monkey();
Bird bird = new Bird(sage);
bird.fly();

  装饰模式的优点:

  1 装饰模式与继承的目的都是扩展对象的功能,但装饰模式更灵活。装饰模式允许动态决定“贴上”一个需要的“装饰”或者除掉一个不需要的“装饰”,而继承是静态的。

  2 具体装饰类的不同组合可以构造出不同行为的组合。

  装饰模式的缺点:

  装饰模式比继承需要更少的类,但是装饰模式比继承产生更多的对象,会使查错变得困难。

  参考资料

  《JAVA与模式》之装饰模式

Java 装饰模式的更多相关文章

  1. java装饰模式

    在java的IO中就是运用装饰模式设计的.一层装饰一层 如:DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(n ...

  2. Java 装饰模式 (Decorator)

    装饰模式 动态的将责任附加到对象上.若要扩展功能,装饰者提供了比继承更有弹性的取代方案 代码 package gx.component; /** * 组件:装饰类和被装饰类 都要继承:为了类型保持一致 ...

  3. Java 装饰模式(4.4)

    装饰模式(decorator pattern). 依照Num模型.讨论职业/IProfession类层次. IProfession定义了方法say(String),事实上现类教师/ Teacher.医 ...

  4. (私人收藏)[开发必备]最全Java离线快速查找手册(可查询可学习,带实例)

    (私人收藏)[开发必备]最全Java离线快速查找手册(可查询可学习,带实例) https://pan.baidu.com/s/1L54VuFwCdKVnQGVc8vD1TQnwmj java手册 Ja ...

  5. Spark案例分析

    一.需求:计算网页访问量前三名 import org.apache.spark.rdd.RDD import org.apache.spark.{SparkConf, SparkContext} /* ...

  6. 装饰模式,制作一个蛋糕java

    import java.text.DecimalFormat; //抽象组件组件 interface mkcake { public void cake(); } class Cake impleme ...

  7. Java设计模式(学习整理)----装饰模式

    1.概念: (在我看来,模式就像是是一种思想,在这种思想的指引下,对代码和结构的一番加工和整合而已!都是套路!) 装饰模式又称包装(Wrapper)模式,是以对客户端透明的方式扩展对象的功能,是继承关 ...

  8. Java设计模式---装饰模式

    装饰模式又名包装(Wrapper)模式.装饰模式以对客户端透明的方式扩展对象的功能,是继承关系的一个替代方案. 装饰模式的结构 装饰模式以对客户透明的方式动态地给一个对象附加上更多的责任.换言之,客户 ...

  9. java模式之装饰模式

    1. 什么叫装饰模式? 根据业务的需求,需要对一个类的方法进行增强的处理. 2. 为什么需要装饰模式? 拓展性更加的好,当觉得这个装饰不好的时候,可以直接拿下,不需要改变任何的代码. 3. 装饰模式的 ...

随机推荐

  1. 让Mysql支持Emoji表情,解决[Err] 1366 - Incorrect string value: '\xF0\xA3\x84\x83'

    mysql insert内容包含表情或者unicode码时候,插入Mysql时失败了,报如下异常: java.sql.SQLException: Incorrect string value: '\x ...

  2. 谷歌浏览器Software Reporter Tool长时间占用CPU解决办法

    什么是Software Reporter Tool Software Reporter Tool是一个Chrome清理工具,用于清理谷歌浏览器中不必要或恶意的扩展,应用程序,劫持开始页面等等.当你安装 ...

  3. XmlSerializer 实现序列化CDATA

    [XmlIgnore] public string GuestRemarks { get; set; } [XmlElement("GuestRemarks")] public X ...

  4. NLog简单例子

    引用 <?xml version="1.0" encoding="utf-8"?> <packages> <package id= ...

  5. Coolpy网络部署说明(宽带互联网)

    本文将介绍Coolpy第二种方案的网络部署方法.以方便大家学习如何让coolpy设备部署到相应的应用场景中.本例将以水星MW310R无线路由器作为演示路由器. 1.硬件连接部分: coolpy设备=& ...

  6. 分布式系统的BASE理论

    一.BASE理论 eBay的架构师Dan Pritchett源于对大规模分布式系统的实践总结,在ACM上发表文章提出BASE理论,BASE理论是对CAP理论的延伸,核心思想是即使无法做到强一致性(St ...

  7. jenkins(5): jenkins邮件报警配置

    参考:  https://blog.csdn.net/u013066244/article/details/78665075 1.  使用 增强版的邮件通知 1.1  安装插件 1.2.  系统配置 ...

  8. BZOJ2142 礼物 扩展lucas 快速幂 数论

    原文链接http://www.cnblogs.com/zhouzhendong/p/8110015.html 题目传送门 - BZOJ2142 题意概括 小E购买了n件礼物,送给m个人,送给第i个人礼 ...

  9. 利用Flume将MySQL表数据准实时抽取到HDFS

    转自:http://blog.csdn.net/wzy0623/article/details/73650053 一.为什么要用到Flume 在以前搭建HAWQ数据仓库实验环境时,我使用Sqoop抽取 ...

  10. P1026 统计单词个数 区间dp

    题目描述 给出一个长度不超过200200的由小写英文字母组成的字母串(约定;该字串以每行2020个字母的方式输入,且保证每行一定为2020个).要求将此字母串分成kk份(1<k \le 401& ...