第一例: 装饰模式是对对象功能增强时,平时使用继承的一种替代方案 一.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(); } } 四.总结 优点:装饰模式和继承都是对功能的扩展,而装饰模式使用的是组合,可以不用继承而达到这一效果.使用过多的继承会增加系统的复杂性和偶合性 缺点:装饰模式要产生一些辅助性的对象,但这些对象看上去都比较像,不是很容易检查(好的命名应该是提高检查的一个办法)
动态地给一个对象添加一些额外的责任。提供一种比用生成子类来构建此行为或责任的更为灵活的方式。 在我看来,装饰模式的重点在于依赖继承和多态特性来实现核心任务和额外工作的统一处理,即被装饰的内容和装饰“一视同仁”。 下面让我们来通过一个实例场景来看看装饰模式到底是怎么一回事。假设你需要打印发票 sales ticket , 发票有抬头、正文和脚注,发票抬头可以是企事业单位,发票号等等,脚注也是一样,可能有很多不同种类的脚注需要打印。如果发票格式固定那也就没必要继续讨论了,现在的问题是,不同的客户需要的发票或者收据的抬头或脚注,他们需要的条目是不一样的,有的需要著明单位,有的只需要发票号,但是脚注需要开票人,等等,对你来说跟现在的 Web 系统一样,客户的要求是动态;不过发票的正文是不会变化的,是固定。要满足这个需求我们有很多种方案,比如你可以抽象一系统对象层次来分层完成这些对象责任。不过我们这里要推荐的是装饰模式,我们来具体看一下,装饰模式是如何工作的:
先看看该场景的 UML 图,
下面看看具体实现
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 ……就是具体的装饰类了。其它的这里就不赘述了。
举个例子,要使用输入流,你首先得挑一款面包,是小麦面粉的,还是大米粉的。比如我们经常从文件中创建流:
结尾时,我们送一款糯米面包心的蛋糕^_^:
Powered by: BlogJava Copyright © Jerome Kwok