Jerome Kwok〖旧日的足迹〗

仁者不忧,知者不惑,勇者不惧
posts - 0, comments - 0, trackbacks - 0, articles - 81
  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理

模式之---装饰模式(Decoratory Pattern )

Posted on 2009-06-29 15:43 Jerome Kwok 阅读(130) 评论(0)  编辑  收藏 所属分类: 设计模式

第一例:
装饰模式是对对象功能增强时,平时使用继承的一种替代方案
一.UML示意图


二.组成部分
1.       抽象构件:原始的功能接口
2.       具体构件:具体的原始功能类
3.       装饰角色:持有具体构件类的对象,以便执行原有功能
4.       具体装饰:具体扩展的功能在这里
三.例子代码:使用装饰模式来演示一个对”行走”功能扩展的例子(听音乐+行走和唱歌+行走)
1. 抽象构件
package com.eekq.decorator;
publicinterface Component {
    /**原始接口*/
    publicvoid go();
}
2. 具体构件
package com.eekq.decorator;
publicclass ConcreteComponent implements Component {
    publicvoid go() 
        System.out.println("行走");
    }
}
3.装饰角色来了
package com.eekq.decorator;
publicclass Decorator implements Component {
    /**持有私有的原始构件*/
    private Component component;
    /**构造子,委派给原始构件*/
    protected Decorator(Component component) {
        this.component = component;
    }
    /**调用原始构件功能,通常就可直接把扩展功能加在此方法中*/
    publicvoid go() {
        this.component.go();
    }
}
4.具体装饰(这里演示了两种扩展的情况,走路+听音乐和唱歌s)
(1).
package com.eekq.decorator;
publicclass ConcreteDecoratorListen extends Decorator {
    /**构造子,相关初始化*/
    public ConcreteDecoratorListen(Component component) {
        super(component);
        // code is here
    }
    /**商业逻辑,对原始构件功能的扩展*/
    publicvoid go() {     
        listen("听音乐");//执行扩展功能
       super.go();
    }
    privatevoid listen(Object obj){
        System.out.println(obj);        
    } 
}
(2).
package com.eekq.decorator;
publicclass ConcreteDecoratorSing extends Decorator {
    /**构造子,相关初始化*/
    public ConcreteDecoratorSing(Component component) {
       super(component);
        // code is here
    }
    /**商业逻辑,对原始构件功能的扩展*/
    publicvoid go() {     
        super.go();
        System.out.println(sing());;// 执行扩展功能
    } 
    private String sing() {
        return"唱歌";
    }
}
5.客户端调用
package com.eekq.decorator;
 publicclass Main {
    publicstaticvoid main(String[] args) {
        /**原始构件*/
        Component component = new ConcreteComponent();
        /**边听音乐,边走路*/
        ConcreteDecoratorListen cdl = new ConcreteDecoratorListen(component);
        cdl.go();
        System.out.println();
        /**边走路,边唱歌*/        ConcreteDecoratorSing cds = new ConcreteDecoratorSing(component);
        cds.go();
    }
}

四.总结
优点:装饰模式和继承都是对功能的扩展,而装饰模式使用的是组合,可以不用继承而达到这一效果.使用过多的继承会增加系统的复杂性和偶合性
缺点:装饰模式要产生一些辅助性的对象,但这些对象看上去都比较像,不是很容易检查(好的命名应该是提高检查的一个办法)


第二例:

按照四人团的说法,装饰模式(Decorator Pattern) 的意图是:

        动态地给一个对象添加一些额外的责任。提供一种比用生成子类来构建此行为或责任的更为灵活的方式。 
        在我看来,装饰模式的重点在于依赖继承和多态特性来实现核心任务和额外工作的统一处理,即被装饰的内容和装饰“一视同仁”。
        下面让我们来通过一个实例场景来看看装饰模式到底是怎么一回事。假设你需要打印发票 sales ticket , 发票有抬头、正文和脚注,发票抬头可以是企事业单位,发票号等等,脚注也是一样,可能有很多不同种类的脚注需要打印。如果发票格式固定那也就没必要继续讨论了,现在的问题是,不同的客户需要的发票或者收据的抬头或脚注,他们需要的条目是不一样的,有的需要著明单位,有的只需要发票号,但是脚注需要开票人,等等,对你来说跟现在的 Web 系统一样,客户的要求是动态;不过发票的正文是不会变化的,是固定。要满足这个需求我们有很多种方案,比如你可以抽象一系统对象层次来分层完成这些对象责任。不过我们这里要推荐的是装饰模式,我们来具体看一下,装饰模式是如何工作的:       

先看看该场景的 UML 图, 

装饰模式之发票案例 

下面看看具体实现

Java代码
1package com.zhaipuhong.designpattern.decorator;   
2  
3public abstract class Component {   
4    public abstract void prtTicket();   
5}
  
6

 1java代码:
 2
 3package com.zhaipuhong.designpattern.decorator;   
 4  
 5import java.util.Date;   
 6  
 7public class SalesTicket extends Component {   
 8  
 9    @Override  
10    public void prtTicket() {   
11        // print sales ticket here   
12        System.out.println("Sales Ticket Body");   
13    }
   
14  
15}
  
16

 1java代码
 2
 3package com.zhaipuhong.designpattern.decorator;   
 4  
 5public abstract class Decorator extends Component {   
 6    private Component myComp;   
 7       
 8    public Decorator(Component myComp) {   
 9        this.myComp = myComp;   
10    }
   
11       
12    @Override  
13    public void prtTicket() {   
14        if(myComp != null)   
15            myComp.prtTicket();   
16    }
   
17  
18}
  
19

 1Java代码
 2
 3package com.zhaipuhong.designpattern.decorator;   
 4  
 5import java.util.Date;   
 6  
 7public class Header1 extends Decorator {   
 8  
 9    public Header1(Component myComp) {   
10        super(myComp);   
11        // TODO Auto-generated constructor stub   
12    }
   
13  
14    @Override  
15    public void prtTicket() {   
16        // place the code of print the sales head   
17        System.out.println("Sales Ticket Header1");   
18        super.prtTicket();   
19    }
   
20       
21}
  
22

 1Java代码
 2
 3package com.zhaipuhong.designpattern.decorator;   
 4  
 5import java.util.Date;   
 6  
 7public class Footer1 extends Decorator {   
 8  
 9    public Footer1(Component myComp) {   
10        super(myComp);   
11        // TODO Auto-generated constructor stub   
12    }
   
13  
14    @Override  
15    public void prtTicket() {   
16        super.prtTicket();   
17        // place the code of print sales foot   
18        System.out.println("Sales Ticket Footer1");   
19    }
   
20  
21}
  
22

 1Java代码
 2
 3package com.zhaipuhong.designpattern.decorator;   
 4  
 5public class Header2 extends Decorator {   
 6  
 7    public Header2(Component myComp) {   
 8        super(myComp);   
 9        // TODO Auto-generated constructor stub   
10    }
   
11  
12    @Override  
13    public void prtTicket() {   
14        // place the code of print the sales head   
15        System.out.println("Sales Ticket Header2");   
16        super.prtTicket();   
17    }
   
18  
19  
20}
  
21

 1 Java代码
 2 
 3 package com.zhaipuhong.designpattern.decorator;   
 4   
 5 public class Footer2 extends Decorator {   
 6   
 7     public Footer2(Component myComp) {   
 8         super(myComp);   
 9         // TODO Auto-generated constructor stub   
10     }   
11   
12     @Override  
13     public void prtTicket() {   
14         // TODO Auto-generated method stub   
15         super.prtTicket();   
16         //place the code of print sales foot   
17         System.out.println("Sales Ticket Footer2");   
18     }   
19   
20 }  
21 

如果你的发票格式为:  

       Sales Ticket Header1
   Sales Ticket Body
   Sales Ticket Footer1
 


   那么你可以这样去创建对象:
   new Header1(new Footer1(new SalesTicket()));       

如果你的发票格式为:

   Sales Ticket Header1
   Sales Ticket Header2
   Sales Ticket Body
   Sales Ticket Footer1 
 

   那么你可以这样去创建对象:
   new Header1(new Header2(new Footer1(new SalesTicket()))); 

看见了吗,你可以自由组合对象的行为,确实非常的神奇。 

    装饰模式为对象添加额外责任的方式就像做蛋糕一样,一圈一圈的加上去,中间的面包是核心,是被装饰的对象(发票正文),是核心任务,外围的都是装饰对象,这就是说装饰模式包含两部分内容,即装饰对象和被装饰对象。特别有趣而又要注意的是,装饰对象不仅可以装饰被装饰对象,而且可以装饰装饰对象,是不是有点绕口,但仔细体会一下,这就是装饰模式的巧妙之处。该特性是在 Decorator 抽象类中体现的,装饰类 Decorator 是用来修饰 Component  对象的,而Component 既是 Decorator 的父类又是 Decorator 的成员变量,所以Decorator 可以装饰Component  的子类(SalesTicket),具体的装饰行为是Decorator 的实现子类来完成的,所以Header1、Footer1等装饰实现类可以用来装饰Component  的子类(如,SalesTicket,它是蛋糕中间的面包),而Header1、Footer1等装饰实现类本身也是Component  的子类,所以……
      装饰模式耍了这么绕,其实就用了两招,继承和多态。第一,所以组件都是Component 的子类,由Component 规划对象的行为(如,public void prtTicket();),装饰类Decorator 与 被装饰类SalesTicket 都是Component 的子类,那么他们具有相同的行为;装饰类Decorator 也以Component 为成员变量,那他就有机会操作Component 的所有子类,而Decorator 的子类都override了Decorator 定义的接口,确切的说是Component 定义的接口,这样,各装饰类有了机会重写自己关心的装饰工作。

 

        下面给出装饰模式的标准UML图

装饰模式之标准图

      

       装饰模式其实就在我们的身边,在J2SE I/O 类库中,大量采用了装饰模式。比如流的使用让很多人比解其妙,当你熟悉了装饰模式,你在使用流就会感觉轻松多了。 

       我们来输入流(Inputstream)来说吧,输出流(OutputStream)跟它是平行设计,举一反三。Inputstream 就是Decorator 模式标准视图的 Component , 像AudioInputStream、ByteArrayInputStream、FileInputStream、PipedInputStream、SequencenputStream、StringBufferInputStream等这些都是蛋糕中的面包,是被装饰的对象,这点与我们的例子有点不同。我们的例子里的蛋糕中面包是小麦面粉做的,流的装饰模式还有大米粉、玉米粉、高粱粉等做的面包,很丰富,哈哈---^_^.  FilterInputStream 就是 Decorator 模式标准视图的 Decorator 了,FilterInputStream 的子类诸如BufferedInputStream, LineNumberInputStream ……就是具体的装饰类了。其它的这里就不赘述了。 

        举个例子,要使用输入流,你首先得挑一款面包,是小麦面粉的,还是大米粉的。比如我们经常从文件中创建流:

1 Java代码
2 
3 new FileReader("test.txt");  

这里蛋糕核心已经挑好了。为了提高性能,我们考虑对文件流做缓冲处理,这是可选的动作,是为文件流额外锦上添花的:
1 Java代码
2 new BufferedReader(new FileReader  ("test.txt"));  

如果你还不满足,你可以继续,输入流中有处理行号的装饰类:
1 Java代码
2 new LineNumberReader(new BufferedReader(new FileReader("test.txt")));  

其实流的构建非常的容易,我想可能是流的面包和装饰在命名上没有一个明显的区分,种类有多,让人眼花缭乱,最终使人望而生畏。这个也是本人时时困惑的地方,每每这样,我就看看这些类的父类就知道该怎么做了,看是否是继承自FilterInputStream ,因为明白装饰模式的原理。

 

       结尾时,我们送一款糯米面包心的蛋糕^_^:

1 Java代码
2 new LineNumberReader(new BufferedReader(new InputStreamReader(socket.getInputStream())));  

到这里我们知道,装饰模式是为现有功能添加附加功能的一种方法。他是一种动态的,自由的组合对象行为的方式。