装饰器(Decorator)

简述:

动态的给一个对象添加一些额外的职责,就增加功能来说,相比生成子类更为灵活。

装饰器(Decorator)模式,是一种在运行期动态给某个对象的实例增加功能的方法。

在IO的Filter模式一节中其实已经讲过装饰器模式了。在Java标准库中。InputStream时抽象类。FileInputStream、ServletInputStream、Socket.getInputStream()这些InputStream都是最终数据源。

现在,要给不同的最终数据源增加缓冲功能,计算签名功能,加密解密功能,那么,3个最终数据源,3种功能一共需要9个子类。如果继续增加最终数据源,或者增加新功能,子类会爆炸式的增长,这种设计方式显然是不可取的。

Docorator模式的目的就是把一个一个的附加功能,用Docotor的方式给一层一层的加到原始的数据源上,最终,通过组合获得我们想要的功能。

例如:给FileInputStream增加缓冲和解压压缩功能,用Docorator模式写醋回来如下:

InputStream fis =new FileInputStream("test.gz");//创建原始数据源
InputStream bis=new BufferedInputStream(fis);//增加缓冲功能
InputSream gis=new GZIPInpitStream(bis);//增加解压缩功能

或者一次性写为这样:

InputStream input=new GZIPInputStream(
            new BufferedInputStream(
                        new FileInputStream("test.gz")                       
             ) ) ;            

观察BufferedInputStream和GZIPInputStream。它们实际上都是从FilterInputStream继承的,这个FilterInputStream就是一个抽象的Decorator。我们用图把Decorator模式画出来如下:

 

 最顶层的Component是接口,对应到IO的就是InputStream这个抽象类。ComponentA,ComponentB是实际的子类,对应到IO的就是FileInputStream、ServletInputStream这些数据源。Decorator是用于实现各个符驾功能的抽象装饰器,对应到IO的就是FilterInputStream。而Docorator派生的就是一个一个的装饰器,它们每个都有独立的功能,对应到IO的就是BufferedInputStream,GZIPInputStream等。

 

Decorator模式有什么好处呢?它实际上把核心功能和附加功能给分开了。核心功能指FileInputStream这些真正读数据的源头,附加功能指加缓冲、压缩、解密这些功能。如果我们要新增核心功能,就增加Component的子类,例如ByteInputStream。如果我们要增加附加功能,就增加Decorator的子类,例如CipherInputStream。两部分都可以独立地扩展,而具体如何附加功能,由调用方自由组合,从而极大地增强了灵活性。

 

如果我们要自己设计完整的Decorator模式,应该如何设计?

假如现在我们要设置一个能够输出Html标签的文本内容,首先我们写一个顶层Component:

 

package 装饰者模式.HTML文本处理样例;

/**
 * @author :wangjiazhi
 * @creatDate:2021年9月2日10点17分
 * @description:这是一个用于写出HTML文本的接口
 */
public interface TextNode {
    /**
     *
     * @param text 要处理的基本内容
     */
    void setText(String text);
    /**
     * @return 返回处理好的文本
     */
    String getText();
}

这个接口定义了基本的文本设置 文本获取。对于核心节点 ,例如<span>标签需要写一个实现类:

package 装饰者模式.HTML文本处理样例;


public class SpanNode implements TextNode{
    String text;
    @Override
    public void setText(String text) {
        this.text=text;
    }

    @Override
    public  String getText() {
    return "<span>"+text+"</span>";
    }
}

可以看到,对于文本进行设置以后,文本就可以达到具有标签的效果了,然而对于一些非核心的标签,但是经常需要用到,实现便于灵活使用的效果,我们要先定义一个装饰者抽象类NodeDecorator:

package 装饰者模式.HTML文本处理样例;

/**
 * @author :wangjiazhi
 * @description:这是一个用于增强自身功能的装饰者类持有一个接口对象
 */
public abstract class NodeDecorator implements TextNode {
    //抽象类继承了接口 不必去实现接口中的所有方法  可以实现一部分 剩下的可以交给子类去实现
    TextNode textNode;
    public NodeDecorator(TextNode textNode){
        this.textNode=textNode;
    }

    @Override
    public void setText(String text) {
        this.textNode.setText(text);
    }
}

可以看到它持有一个TextNode对象,有点像代理模式。对于装饰者模式与代理模式,继承之间的联系,差异,我会在我的下一篇随笔中写到,本次只是简单的使用,只了解这种思想。
非核心的加粗标签装饰器如下:

package 装饰者模式.HTML文本处理样例;

/**
 * @author :wanhjiazhi
 * @creatDate:2021年9月2日10点17分
 * @description:这是一个用于加粗的装饰者类
 */
public class BoldDecorator extends NodeDecorator {
    public BoldDecorator(TextNode textNode){
        super(textNode);
    }
    @Override
    public String getText() {
        return "<b>"+textNode.getText()+"</b>";
    }
}

非核心的斜体标签装饰器如下:

package 装饰者模式.HTML文本处理样例;

public class ItalicDecorator extends NodeDecorator{
    public ItalicDecorator(TextNode textNode){
        super(textNode);
    }    @Override
    public String getText() {
        return "<i>"+textNode.getText()+"</i>";
    }
}

我们进行代码的演示:

package 装饰者模式.HTML文本处理样例;

public class Main {
    public static void main(String[] args) {
        TextNode textNodeSpan=new SpanNode();
        TextNode textNodeBoldSpan=new BoldDecorator(new SpanNode());
        TextNode textNodeItalicBoldSpan=new ItalicDecorator(new BoldDecorator(new SpanNode()));
        textNodeSpan.setText("hello world");
        textNodeBoldSpan.setText("hello world");
        textNodeItalicBoldSpan.setText("hello world");
        System.out.println(textNodeSpan.getText());//<span>hello world</span>
        System.out.println(textNodeBoldSpan.getText());//<b><span>hello world</span></b>
        System.out.println(textNodeItalicBoldSpan.getText());//<i><b><span>hello world</span></b></i>
    }
}

 

对于这种模式的理解:可以看出来,可以灵活的层层嵌套达到各种标签的搭配,相比单纯继承实现更加灵活。

结构图如下:

结语:

使用Decorator模式,可以独立增加核心功能,也可以独立增加附加功能,二者互不影响;

可以在运行期动态地给核心功能增加任意个附加功能。

 

posted @ 2021-09-01 16:42  王佳智  阅读(379)  评论(0)    收藏  举报