• Java 装饰模式


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

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

      装饰模式的类图如下:

      

      涉及到的角色:

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

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

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

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

      抽象构件角色:

    1 public interface Component {
    2     
    3     public void sampleOperation();
    4     
    5 }

      具体构件角色:

    1 public class ConcreteComponent implements Component {
    2 
    3     @Override
    4     public void sampleOperation() {
    5         // 写相关的业务代码
    6     }
    7 
    8 }

      装饰角色:

     1 public class Decorator implements Component{
     2     private Component component;
     3     
     4     public Decorator(Component component){
     5         this.component = component;
     6     }
     7 
     8     @Override
     9     public void sampleOperation() {
    10         // 委派给构件
    11         component.sampleOperation();
    12     }
    13     
    14 }

      具体装饰角色:

     1 public class ConcreteDecoratorA extends Decorator {
     2 
     3     public ConcreteDecoratorA(Component component) {
     4         super(component);
     5     }
     6     
     7     @Override
     8     public void sampleOperation() {
     9      super.sampleOperation();
    10         // 写相关的业务代码
    11     }
    12 }
     1 public class ConcreteDecoratorB extends Decorator {
     2 
     3     public ConcreteDecoratorB(Component component) {
     4         super(component);
     5     }
     6     
     7     @Override
     8     public void sampleOperation() {
     9       super.sampleOperation();
    10         // 写相关的业务代码
    11     }
    12 }

      齐天大圣:

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

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

      

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

    1 //大圣的尊号
    2 public interface TheGreatestSage {
    3     
    4     public void move();
    5 }

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

    1 public class Monkey implements TheGreatestSage {
    2 
    3     @Override
    4     public void move() {
    5         //代码
    6         System.out.println("Monkey Move");
    7     }
    8 
    9 }

      抽象装饰角色七十二变:

     1 public class Change implements TheGreatestSage {
     2     private TheGreatestSage sage;
     3     
     4     public Change(TheGreatestSage sage){
     5         this.sage = sage;
     6     }
     7     @Override
     8     public void move() {
     9         // 代码
    10         sage.move();
    11     }
    12 
    13 }

      具体装饰角色鱼儿:

     1 public class Fish extends Change {
     2     
     3     public Fish(TheGreatestSage sage) {
     4         super(sage);
     5     }
     6 
     7     @Override
     8     public void move() {
     9         // 代码
    10         System.out.println("Fish Move");
    11     }
    12 }

      具体装饰角色鸟儿:

     1 public class Bird extends Change {
     2     
     3     public Bird(TheGreatestSage sage) {
     4         super(sage);
     5     }
     6 
     7     @Override
     8     public void move() {
     9         // 代码
    10         System.out.println("Bird Move");
    11     }
    12 }

      客户端:

     1 public class Client {
     2 
     3     public static void main(String[] args) {
     4         TheGreatestSage sage = new Monkey();
     5         // 第一种写法
     6         TheGreatestSage bird = new Bird(sage);
     7         TheGreatestSage fish = new Fish(bird);
     8         // 第二种写法
     9         //TheGreatestSage fish = new Fish(new Bird(sage));
    10         fish.move(); 
    11     }
    12 
    13 }

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

      

      

      装饰模式的简化

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

      

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

      

      透明性的要求:

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

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

      正确做法:

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

      错误做法:

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

      半透明的装饰模式:

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

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

      

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

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

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

      装饰模式的优点:

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

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

      装饰模式的缺点:

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

      参考资料

      《JAVA与模式》之装饰模式

  • 相关阅读:
    oracle学习13
    oracle学习12
    oracle学习11
    oracle学习10
    CodeForces
    CodeForces
    UVA
    poj3320 Jessica's Reading Problem
    poj2456 Aggressive cows
    jQuery 鼠标滚轮插件 mousewheel
  • 原文地址:https://www.cnblogs.com/WJQ2017/p/7722261.html
Copyright © 2020-2023  润新知