2023-03-27
原文作者:ClarenceZero 原文地址:https://blog.csdn.net/ClarenceZero/article/details/106915912

装饰者模式

定义

装饰者模式动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。

(引用 Head First 设计模式)

一种动态地往一个类中添加新的行为的设计模式。就功能而言,修饰模式相比生成子类更为灵活,这样可以给某个对象而不是整个类添加一些功能。

通过使用修饰模式,可以在运行时扩充一个类的功能。原理是:增加一个修饰类包裹原来的类,包裹的方式一般是通过在将原来的对象作为修饰类的构造函数的参数。装饰类实现新的功能,但是,在不需要用到新功能的地方,它可以直接调用原来的类中的方法。修饰类必须和原来的类有相同的接口。

(引用 维基百科)

这种类型属于结构型模式,它是作为现有的类的一个包装。创建一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。

类图

202303272242205161.png

  • Component

    被装饰者抽象类。

  • ConcreteComponent

    动态地加上新行为的对象。继承Component。

  • Decorator

    装饰者。每个装饰者都有一个组件。持有被装饰者的引用。且需要继承被装饰者。

    我们复用继承达到类型匹配,而不是复用继承获得了行为。当我们将装饰者与组件组合时,就是在加入新的行为。所得到的新行为,并不是继承自超类,而是由组合对象得来的。继承Beverage抽象类,是为了有正确的类型,而不是继承它的行为。行为来自装饰者和基础组件,或与其他装饰者之间的组合关系。

  • ConcreteDecorator

    具体的装饰者。

实现

202303272242210852.png

1. 定义Component抽象类

    /**
     * 饮料抽象类
     * 被装饰者抽象类
     */
    public abstract class Beverage {
        /**
         * 描述字段
         */
        String description = "Unknown Beverage";
        public String getDescription() {
            return description;
        }
    
        /**
         * 待每个不同类型的子类实现的方法
         * @return
         */
        public abstract double cost();
    }

2. 定义Decorator抽象类

    /**
     * 装饰者抽象类
     * 所有的辅料都要继承该类
     * 必须让Condiment Decorator能取代Beverage
     */
    public abstract class CondimentDecorator extends Beverage{
        /**
         * 所有子类必须重新实现该方法。因为装饰器需要保持被装饰者的类方法签名完整
         * @return
         */
        public abstract String getDescription();
    }

3. 定义Component实现类

    /**
     * 被装饰者实现类
     * 浓咖啡
     */
    public class Espresso extends Beverage {
        public Espresso() {
            description = "Espresso";
        }
    
        @Override
        public double cost() {
            return 1.99;
        }
    }

4. 定义Decorator实现类

    /**
     * 摩卡是一个装饰者,对Beverage进行装饰
     * 该类清楚知道被装饰对象。因为需要持有被装饰对象引用
     * 1.持有被装饰者的引用
     * 2.保证被装饰者的方法签名的实现(getDescription())
     * 3.实现装饰(cost)
     */
    public class Mocha extends CondimentDecorator{
        Beverage beverage;
        public Mocha(Beverage beverage) {
            this.beverage = beverage;
        }
    
        @Override
        public String getDescription() {
            return beverage.getDescription() + ", Mocha";
        }
    
        @Override
        public double cost() {
            return .20 + beverage.cost();
        }
    }
    
    /**
     * 1.持有被装饰者的引用
     * 2.保证被装饰者的方法签名的实现(getDescription())
     * 3.实现装饰(cost)
     */
    public class Whip extends CondimentDecorator{
        Beverage beverage;
        public Whip(Beverage beverage) {
            this.beverage = beverage;
        }
    
        @Override
        public String getDescription() {
            return beverage.getDescription() + ", Whip";
        }
    
        @Override
        public double cost() {
            return .66 + beverage.cost();
        }
    }

5. Main方法

    public class T {
        public static void main(String[] args) {
            // 创建基础类
            Beverage beverage = new Espresso();
            System.out.println(beverage.getDescription() + " $" + beverage.cost());
            // 用摩卡装饰
            Mocha mocha = new Mocha(beverage);
            System.out.println(mocha.getDescription() + " $" + mocha.cost());
            // 用Whip装饰Mocha
            Whip whip = new Whip(mocha);
            System.out.println(whip.getDescription() + " $" + whip.cost());
        }
    }
    Espresso $1.99
    Espresso, Mocha $2.19
    Espresso, Mocha, Whip $2.8

浅析 Java IO 装饰模式

类图

202303272242216493.png

引用 https://www.cnblogs.com/LUA123/p/10685693.html

  • 抽象组件InputStream

    这个抽象类为各种子类型流处理器提供统一的接口。

  • 具体组件

    FileInputStream、ObjectInputStream 、ByteArrayInputStream等原始流处理器扮演,他们实现了InputStream的接口,可以被装饰器装饰。

  • 抽象装饰类

    FilterInputStream,也实现了InputStream的接口。

  • 具体装饰者

    DataInputStream 、BufferedInputStream

Java IO流分类

流分类 使用分类 字节输入流 字节输出流 字符输入流 字符输出流
抽象基类 InputStream OutputStream Reader Writer
节点流 访问文件  FileInputStream   FileOutStream   FileReader   FileWriter 
节点流 访问数值  ByteArrayInputStream   ByteArrayOutStream   CharArrayReader   CharArrayWriter 
节点流 访问管道  PipedInputStream   PipedOutStream   PipedReader   PipedWriter 
节点流 访问字符串  StringReader   StringWriter 
处理流 缓冲流 BufferedInputStream BufferedOutputStream BufferedReader BufferedWriter
处理流 转换流 InputStreamReader OutputStreamWriter
处理流 对象流 ObjectInputStream ObjectOutputStream
处理流 抽象基类(过滤) FilterInputStream FilterOutputStream FilterReader FilterWriter
处理流 打印流 PrintStream PrintWriter
处理流 推回输入流 PushbackInputStream PushbackReader
处理流 特殊流 DataInputStream DataOutputStream

节点流和处理流

节点流是真正处理数据的。处理流是装饰者。

  • 节点流

    • 文件流:FileInputStream,FileOutputStrean,FileReader,FileWriter,底层调用native方法实现文件读取、写入功能。
    • 数组流:ByteArrayInputStream,ByteArrayOutputStream,CharArrayReader,CharArrayWriter,对数组进行处理的节点流。
    • 字符串流:StringReader,StringWriter,其中 StringReader 能从 String 中读取数据并保存到 char 数组。
    • 管道流:PipedInputStream,PipedOutputStream,PipedReader,PipedWrite,对管道进行处理的节点流。
  • 处理流

    • 缓冲流 :BufferedImputStrean,BufferedOutputStream,BufferedReader ,BufferedWriter,需要父类作为参数构造,增加缓冲功能,避免频繁读写硬盘,可以初始化缓冲数据的大小,由于带了缓冲功能,所以就写数据的时候需要使用 flush 方法,另外,BufferedReader 提供一个 readLine( ) 方法可以读取一行,而 FileInputStream 和 FileReader 只能读取一个字节或者一个字符,因此 BufferedReader 也被称为行读取器。
    • 转换流:InputStreamReader,OutputStreamWriter,要 inputStream 或 OutputStream 作为参数,实现从字节流到字符流的转换,我们经常在读取键盘输入(System.in)或网络通信的时候,需要使用这两个类。
    • 数据流:DataInputStream,DataOutputStream,提供将基础数据类型写入到文件中,或者读取出来。

实例

    Reader reader = new FileRead();

小结

  1. 装饰者和被装饰对象有相同的超类型。所以在任何需要原始对象(被包装的)的场合,可以用装饰过的对象代替它。我们复用继承达到类型匹配,而不是复用继承获得了行为

  2. 你可以用一个或多个装饰者包装一个对象。

  3. 装饰者可以在所委托被装饰者的行为之前与/或之后,加上自己的行为,以达到特定的目的。

  4. 对象可以在任何时候被装饰,所以可以在运行时动态地、不限量地用你喜欢的装饰者来装饰对象。

  5. 局限性

    1. 导致程序中增加许多功能类似的很小的类
阅读全文