装饰模式

装饰模式简单理解就是动态给一个对象添加额外的职责。虽然编写子类也能实现这种功能,但是子类没有装饰模式灵活。Java的IO类就是最典型的装饰模式的应用:

BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream("aaa.txt"));

我们知道,BufferedInputStream继承FilterInputStream,后者又继承FileInputStream,这里可以将BufferedInputStream理解成一个装饰器,它为FileInputStream添加了更多的功能,但是也不影响直接使用FileInputStream。而除了BufferedInputStream,还有其他装饰器可供选用,使用者可以自由组合。

装饰模式示例

下面代码展示了一个典型的装饰模式的例子。

Message.java

public interface Message {
    public String getMsg();
}

MessageConcrete.java

public class MessageConcrete implements Message {
    private String msg;

    public MessageConcrete(String msg) {
        this.msg = msg;
    }

    public String getMsg() {
        return msg;
    }
}

Base64Message.java

public class Base64Message implements Message {
    private Message message;

    public Base64Message(Message message) {
        this.message = message;
    }

    @Override
    public String getMsg() {
        String msg = this.message.getMsg();
        return EncodeUtil.base64encode(msg);
    }
}

EncryptedMessage.java

public class EncryptedMessage implements Message {
    private Message message;

    public EncryptedMessage(Message message) {
        this.message = message;
    }

    @Override
    public String getMsg() {
        String msg = this.message.getMsg();
        return EncodeUtil.encrypt(msg);
    }
}

Main.java

public class Main {
    public static void main(String[] args) throws Exception {
        Base64Message base64Message = new Base64Message(new EncryptedMessage(new MessageConcrete("hello")));
        base64Message.getMsg();
    }
}

我们从使用者的角度看上述代码中用装饰模式封装的类。首先使用者创建MessageConcrete,这个是我们要表达的Message的实体,然后分别用EncryptedMessageBase64Message对其进行装饰,最后得到base64Message实例。最后调用getMsg()获得我们需要的信息。

如果使用者此时不想进行BASE64编码了呢?他只需要去掉Base64Message的装饰即可,在EncryptedMessage上调用getMsg()。如果使用者又不想加密了,那就直接在MessageConcrete上调用getMsg()。如果使用者想换一下加密和BASE64编码的顺序,也是完全可以做到的,调换一下装饰器封装的顺序就行了。

装饰模式实现

通过上面代码我们发现,装饰器模式非常灵活,它能让用户定制一系列“装饰”操作,用户可以自由的组合装饰器,我们现在分析一下如何实现装饰器模式。

UML如图所示,首先我们需要一个接口,我们操作的真正的实体类,和装饰器类都实现该接口,装饰器类中又定义了实体类作为属性,因此可以通过构造函数或者set方法,把实体类传入装饰器。

最重要的是当调用装饰器中的接口方法时,装饰器首先调用实体类的该方法,然后对其返回结果进行装饰。

@Override
public String getMsg() {
    String msg = this.message.getMsg();
    return EncodeUtil.encrypt(msg);
}

最终,我们实现了灵活好用的装饰模式。

装饰模式的结构微调

实际上,实现装饰模式不必严格遵守某一个结构,重要的是用户使用时有一致的体验。

对于上述代码,如果我们觉得想增强装饰器的可扩展性,可以从所有装饰器中抽取出一个装饰器抽象类。如果装饰器的构造函数有更重要的任务,装饰器类中实体对象的引用也可以使用set方法传入而不是构造函数传入。

作者:Gacfox
版权声明:本网站为非盈利性质,文章如非特殊说明均为原创,版权遵循知识共享协议CC BY-NC-ND 4.0进行授权,转载必须署名,禁止用于商业目的或演绎修改后转载。