装饰器(Decorator)模式

  |   0 评论   |   0 浏览

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

在Java 标准库中, InputStream 是抽象类,FileInputStream、ServletInputStream、Socket.getInputStream() 这些InputStream 都是最终数据源.

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

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

例如:给 FileInputStream 增加缓冲和解压功能,用 Decorator 模式写出如下:

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

用图把 Decorator 模式画出来如下:

image.png

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

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

举个例子:假设我们需要渲染一个 HTML 的文本,但是文本还可以附加一些效果,比如加粗、变成斜体、加下划线等。为了实现动态附加效果,可以采用 Decorator 模式。

首先,仍然需要定义顶层结构 TextNode:

public interface TextNode{
   //设置 text
   void setText(String text);
   // 获取 text  
   String getText();
}

对于核心节点,例如<span> 它需要从 TextNode 直接继承:

public class SpanNode implements TextNode{
    private String text;
  
    public void setText(String text){
       this.text = text;
    }
  
    public String getText(){
       return "<span>" + text + "</span>";
    }
}

紧接着,为了实现 Decorator 模式,需要有一个抽象的 Decorator 类:

public abstract classs NodeDecorator implements TextNode{
    protected final TextNode target;

    protected NodeDecorator(TextNode target){
        this.target = target;
    } 
   
    public void setText(String text){
       this.target.setText(text);
    }
}

这个 NodeDecorator 类的核心是持有一个 TextNode, 即将要把功能附加到的 TextNode 实例。接下来就可以写一个加粗功能:

public class BoldDecorator extends NodeDecorator{
   public BoldDecorator(TextNode target){
       super(target)
   }
  
   public String getText(){
       return "<b>" + target.getText() + "</b>";
   }

}

类似的,可以继续加 ItalicDecorator、UnderlineDecorator 等。客户端可以自由组合这些 Decorator:

TextNode n1 = new SpanNode();
TextNode n2 = new BoldDecorator(new UnderlineDecorator(new SpanNode()))
TextNode n3 = new ItalicDecorator(new BoldDecorator(new SpanNode()))
n1.setText("Hello");
n2.setText("Decorated");
n3.setText("World")

System.out.println(n1.getText());
// 输出 <span>Hello</span>
System.out.println(n2.getText());
// 输出<b> <u><span>Decorated</span></u></b>
System.out.println(n3.getText());
// 输出 <i><b><span>World</span></b></i>


标题:装饰器(Decorator)模式
作者:zh847707713
地址:http://lovehao.cn/articles/2020/11/07/1604732978863.html