装饰者模式拥有一个设计非常巧妙的结构,它可以动态添加对象功能。在基本的设计原则中,有一条重要的设计准则叫做合成/聚合复用原则。根据该原则的思想,代码复用应该尽可能使用委托,而不是使用继承。因为继承是一种紧耦合,任何父类的改动都会影响其子类,不利于系统维护。而委托则是松散耦合,只要接口不变,委托类的改动不会影响其上层对象。
装饰者模式充分利用这种思想,通过委托机制,复用系统中的各个组件,在运行时,可以将这些功能进行叠加,从而构造一个超级对象,使其拥有这些组件的功能。而各个子功能模块,被很好的维护在各个组件的相关类中,拥有整洁的系统结构。
装饰者模式可以有效分离性能组件和功能组件,从而提升模块的可维护性并增加模块的复用性。
角色:组件接口—-组件接口是装饰者和被装饰者的超类或者接口,定义了被装饰者的核心功能和装饰者需要加强的功能点;具体组件—-具体组件实现了组件接口的核心方法,完成某一个具体的业务逻辑,也是被装饰的对象;装饰者—-实现组件接口,并持有一个具体的被装饰者对象;具体装饰者—-具体实现装饰的业务逻辑,即实现了被分离的各个增强功能点,各个具体装饰者是可以相互叠加的,从而可以构成一个功能更加强大的组件对象。
装饰者和被装饰者拥有相同的接口,被装饰者通常是系统的核心组件,完成特定的功能目标,而装饰者则可以在被装饰者的方法前后,加上特定的前置处理和后置处理,增强被装饰者的功能。
装饰者的一个典型案列是对输出结果进行增强。例,现在需要将某一结果通过HTML进行发布,则首先需要将内容转换为Html文本,同时由于内容需要在网络上通过Http流传,所以还需要增加Http头。装饰者模式的核心在于:无需将所有的逻辑,即核心内容构建、HTML文本构造和HTTP头生成等3个功能模块粘合在一起实现。通过装饰者,将他们分解为3个几乎完全独立的组件2,并在使用时灵活的进行装配。
组件接口:
/**
* 组件接口 定义了被装饰者的核心功能和装饰者需要增强的功能点
* @author qiaolin
*
*/
public interface IPacketCreator {
String handleContent();//用于内容处理
}
具体组件–被装饰者:
package com.performance.optimization.design.decorator;
/**
* 被装饰者(具体组件) 返回数据包的核心数据 也是主要的业务处理
* @author qiaolin
*
*/
public class PacketBodyCreator implements IPacketCreator{
public String handleContent() {
return "Content of Packet";
}
}
装饰者— 抽象类:
package com.performance.optimization.design.decorator;
/**
* 装饰者 维护核心组件对象,负责告诉其子类,其核心业务逻辑处理全权委托给组件对象完成,自己仅作增强处理
* @author qiaolin
*
*/
public abstract class PacketDecorator implements IPacketCreator{
protected IPacketCreator component;
public PacketDecorator(IPacketCreator component){
this.component = component;
}
}
具体装饰者:
package com.performance.optimization.design.decorator;
/**
* 具体的装饰器 负责对核心内容进行html格式化操作,委托了组件进行核心业务处理
* @author qiaolin
*
*/
public class PacketHTMLHeaderCreator extends PacketDecorator {
public PacketHTMLHeaderCreator(IPacketCreator component) {
super(component);
}
/**
* 封装数据为html
*/
public String handleContent() {
StringBuffer buffer = new StringBuffer();
buffer.append("<html>");
buffer.append("<body>");
buffer.append(component.handleContent());//业务逻辑处理
buffer.append("</body>");
buffer.append("</html>\n");
return buffer.toString();
}
}
package com.performance.optimization.design.decorator;
import java.util.Date;
/**
* 具体的装饰器 负责对数据头加上HTTP头信息
* @author qiaolin
*
*/
public class PacketHTTPHeaderCreator extends PacketDecorator{
public PacketHTTPHeaderCreator(IPacketCreator component) {
super(component);
}
/**
* 对给定数据加上消息头
*/
public String handleContent() {
StringBuffer buffer = new StringBuffer();
buffer.append("Cache-Control:no-cache\n");
buffer.append("Date:" + new Date() + "\n");
buffer.append(component.handleContent());
return buffer.toString();
}
}
装饰者模式测试:
/**
* 将装饰者和被装饰者组装在一起
*/
@Test
public void test(){
IPacketCreator component = new PacketHTTPHeaderCreator(
new PacketHTMLHeaderCreator(new PacketBodyCreator()));
System.out.println(component.handleContent());
}
装饰者模式测试结果:
Cache-Control:no-cache
Date:Mon Aug 08 13:42:01 CST 2016
<html><body>Content of Packet</body></html>
通过装饰者的构造函数,将被装饰者对象传入。生成的对象实例从最里面开始创建,即作为核心组件最先被创建。在JDK的实现中,有不少组件也是使用装饰者模式实现的,其中一个典型的例子是OutputStream和InputStream类族的实现。其中组件接口是OutputStream这个抽象类,FileOutputStream是装饰者也是被装饰者,具体的装饰者是FileOutputStream的子类,