设计模式---装饰者模式

      装饰者模式拥有一个设计非常巧妙的结构。它可以动态添加对象功能。在基本的设计原则中,有一条重要的设计准则叫做合成、聚合复用原则。根据该原则的思想,代码复用应该尽可能使用委托,二部是使用继承。因为继承是一种紧密耦合,任何父类的改动都会影响子类。不利于系统维护。二委托则是松散耦合,只要接口不变,委托类的改动并不会影响其上层对象。

     装饰者模式就充分运用了这种思想,通过委托机制,复用了系统中的各个组件,在运行时,可以将这些功能组件进行叠加,从而构造一个“超级对象”,使其拥有所有这些组件的功能。而各个子功能模块,被很好地维护在各个组件的相关类中,拥有整洁的系统结构。

      装饰者模式的基本结构如下图所示。

设计模式---装饰者模式

装饰者和被装饰者拥有相同的接口。被装饰者通常是系统的核心组件,完成特定的功能目标。而装饰者可以在被装饰者的方法前后,加上特定的前置处理和后置处理,增强被装饰者的功能。

      装饰者模式的一个典型案例就是对输出结果进行增强。比如,现在需要将某一结果通过HTML进行发布,那么首先就需要将内容转化成一个HTML文本。同时,由于内容需要在网络上通过HTTP流传,故,还需要为其增加一个HTTP头。当然,作为一个更复杂的情况,可能还要为其安置TCP头等。但作为一个示例,这里做简化处理。

     装饰者模式的核心思想在于:无需将所有的逻辑,即,核心内容的构建、HTML文本构造和HTTP头生成等三个功能模块粘合在一起实现。通过装饰者模式,可以将它们分解为三个几乎完全独立的组件,并在使用的时候灵活地进行配置。为实现这个功能,可以使用如图所示的结构。

设计模式---装饰者模式设计模式---装饰者模式

IPacketCreator即装饰接口,用于处理具体的内容。PacketBodyCreator是具体的组件,它的功能是构造要发布的信息的核心内容,但是它不负责将其构造成一个格式工整、可直接发布的数据格式。PacketHTTPHeaderCreator负责对给定的内容加上HTTP头部,PacketHTMLHeaderCreator负责借给定的接口格式化成HTML文本。3个功能模块相对独立且分离,易于维护。

    IPacketCreator的实现很简单,它是一个单方法的接口:

public interface IPackatCreator {
    public String handleContent(); //处理内容
}

PacketBodyCreator用于返回数据包的核心数据:

public class PacketBodyCreator implements IPackatCreator {

    public String handleContent() {
        return "Content of Packet";
    }
}

PacketDecorator维护核心组件component对象,它负责告知其子类,其核心业务逻辑应该全权委托component完成,自己仅仅是做增强处理。

public abstract class PacketDecorator implements IPackatCreator{

    IPackatCreator component;
    public PacketDecorator(IPackatCreator c){
        component = c;
    }
}

PacketHTMLHeaderCreator是具体的装饰器,它负责对核心发布的内容进行HTML格式化操作。需要特别注意的是,它委托了具体组件component进行核心业务处理。

public class PacketHTMLHeaderCreator extends PacketDecorator {


    public PacketHTMLHeaderCreator(IPackatCreator c) {
        super(c);
    }

    public String handleContent() {
        StringBuilder sb = new StringBuilder();
        sb.append("<html>");
        sb.append("<body>");
        sb.append(component.handleContent());
        sb.append("</body>");
        sb.append("</html>\n");
        return sb.toString();
    }
}

PacketHTTPHeaderCreator与PacketHTMLHeaderCreator类似,但是它完成数据包HTTP头部的处理。其余业务处理依然交由内部的component完成。

public class PacketHTTPHeaderCreator extends PacketDecorator {
    public PacketHTTPHeaderCreator(IPackatCreator c) {
        super(c);
    }

    public String handleContent() {
        StringBuilder sb = new StringBuilder();
        sb.append("Cache-Control: no-cache\n");
        sb.append("Date:Mon,31Dec201203:25:57GMT\n");
        sb.append(component.handleContent());

        return sb.toString();
    }
}

对于装饰者模式,另一个值得注意的地方是它的使用方法。在本利中,通过层层构造和组装这些被装饰者到一个对象中。使其有机地结合在一起工作。

public class Main {

    public static void main(String[] args){
        IPackatCreator pc = new PacketHTTPHeaderCreator(new PacketHTMLHeaderCreator(new PacketBodyCreator()));
        System.out.println(pc.handleContent());
    }
}

可以看到,通过装饰者的构造函数,将被装饰对象转入。本例中,共生成3个对象实例,作为核心组件的PacketBodyCreator是最先被构造的,其次PacketHTMLHeaderCreator,最后才是PacketHTTPHeaderCreator。

这个顺序表示,首先由PacketBodyCreator对象去生成核心发布内容,接着由PacketHTMLHeaderCreator对象对这个内容进行处理,将其转化为HTML,最后由PacketHttpHeaderCreator对PacketHTMLHeaderCreator的输出安置HTTP头部。程序运行的输出如下。

Cache-Control: no-cache
Date:Mon,31Dec201203:25:57GMT
<html><body>Content of Packet</body></html>

在JDK中,有不少组件也是用装饰者模式实现的。其中,一个最典型的例子就是OutputStream和InputStream类族的实现。以OutputStream为例,OutputStream对象提供的方法比较简单,功能比较弱,但通过各种装饰者的增强,OutputStream对象可以被赋予强大的功能。