天天看点

[设计模式] 结构型:装饰器模式(Decorator Pattern)

文章目录

    • 什么是装饰器模式
    • 设计与实现
      • 基本接口与文件内容
      • 读取一个单词
      • 读取一行内容
      • 读取所有内容
    • 装饰器模式与代理模式的区别

什么是装饰器模式

房子装饰的再好看,它也还是个房子,只是住着比不装修的房子更舒服。

女人打扮的花枝招展,她也还是个女人,但她比素颜女人对男人更加有吸引力。

装饰器模式的概念就是这样,通过对具体对象进行不断的装饰,使其具备更强大的能力,但究其本身,依旧还是那个对象类的概念。

父子类继承关系,可以从类的层面上扩展对象功能,但是继承本身又让类之间有了耦合联系。装饰器模式可以看作是继承的替代方案之一,它是基于对象层面去扩展新功能。

Java

里面,

I/O

标准库的设计就很好的展示了装饰器模式的魅力,如果有兴趣可以去看源码,仔细研究体会。

设计与实现

我们不具体分析

Java I/O

展示出来的装饰器模式,但是可以基于它的基本方法,尝试着用装饰器模式实现一个场景。

假设现在有这么一个场景,要读取一个文件的内容,提供三个读取方法

read()

,依次满足:

  1. 一次可以读取一个单词
  2. 一次可以读取一行内容
  3. 一次可以读取整个文件内容

怎么设计代码实现这个场景呢?第一条最好实现,第二条可以建立在第一条之上,第三条可以建立在第二条之上。也就是说,实现的思路是“依次装饰”。

基本接口与文件内容

根据需求场景,可以定义一个简单的接口规范:

public interface StringInputStream {
	// 读取文件内容(一个单词、一行内容、所有内容)
    String read() throws IOException;
}
           

文件内容,这是

Mac

系统下编辑的文件,行结尾的符号是

换行键

,对应的

ASCII

码是

10

,空格符对应的

ASCII

码是

32

hello word
I tell you
You are beautiful
           

所以,请注意,以下程序适用于文件换行符是

换行键

的文件,其它文件格式不适用,需要修改!!!

读取一个单词

我们以

Java

自有的

FileInputStream

为基础,就可以很容易的实现这个需求:

// 一次读取一个单词
public class WordInputStream implements StringInputStream {
    private FileInputStream fileInputStream;

    public WordInputStream(FileInputStream inputStream) {
        this.fileInputStream = inputStream;
    }

    protected StringBuilder read0() throws IOException {
        StringBuilder sb = new StringBuilder();
        int read;
        while ((read = fileInputStream.read()) != -1) {
            sb.append((char) read);
            if (read == 32 || read == 10) break;
        }
        return sb.length() == 0 ? null : sb;
    }

	@Override
    public String read() throws IOException {
        StringBuilder sb = read0();
        if (sb == null) return null;
        int charAt = sb.charAt(sb.length() - 1);
        if (charAt == 32 || charAt == 10) return sb.substring(0, sb.length() - 1);
        return sb.toString();
    }
}

// 使用方法
public static void main(String[] args) throws IOException {
    FileInputStream fileInputStream = new FileInputStream("word.txt");
    WordInputStream wordInputStream = new WordInputStream(fileInputStream);
    String word = wordInputStream.read();
}
           

读取一行内容

包装一下

WordInputStream

类,就可以很容易的读取一行数据:

// 一次读取一行内容
public class LineInputStream implements StringInputStream {
    private WordInputStream wordInputStream;

    public LineInputStream(WordInputStream inputStream) {
        this.wordInputStream = inputStream;
    }

    protected StringBuilder read0() throws IOException {
        StringBuilder sb = new StringBuilder(), temp;
        while ((temp = wordInputStream.read0()) != null) {
            sb.append(temp);
            if (temp.charAt(temp.length() - 1) == 10) break;
        }
        return sb.length() == 0 ? null : sb;
    }

    @Override
    public String read() throws IOException {
        StringBuilder sb = read0();
        if (sb == null) return null;
        else return sb.toString();
    }
}

// 使用方法
public static void main(String[] args) throws IOException {
    FileInputStream fileInputStream = new FileInputStream("word.txt");
    WordInputStream wordInputStream = new WordInputStream(fileInputStream);
    LineInputStream lineInputStream = new LineInputStream(wordInputStream);
    String word = lineInputStream.read();
}
           

读取所有内容

包装一下

LineInputStream

类,就可以很容易的读取一行数据:

// 一次读取所有内容
public class WholeInputStream implements StringInputStream {
    private LineInputStream lineInputStream;

    public WholeInputStream(LineInputStream lineInputStream) {
        this.lineInputStream = lineInputStream;
    }

    protected StringBuilder read0() throws IOException {
        StringBuilder sb = new StringBuilder(), temp;
        while ((temp = lineInputStream.read0()) != null) {
            sb.append(temp);
        }
        return sb.length() == 0 ? null : sb;
    }

    @Override
    public String read() throws IOException {
        StringBuilder sb = read0();
        if (sb == null) return null;
        else return sb.toString();
    }
}

// 使用方法
public static void main(String[] args) throws IOException {
    FileInputStream fileInputStream = new FileInputStream("word.txt");
    WordInputStream wordInputStream = new WordInputStream(fileInputStream);
    LineInputStream lineInputStream = new LineInputStream(wordInputStream);
    WholeInputStream wholeInputStream = new WholeInputStream(lineInputStream);
    String word = wholeInputStream.read();
}
           

以上程序写的较为简单,但是基本也能表达出装饰器模式的用途,可以认为:

  1. WordInputStream

    装饰了

    FileInputStream

  2. LineInputStream

    装饰了

    WordInputStream

  3. WholeInputStream

    装饰了

    LineInputStream

装饰器模式与代理模式的区别

装饰器模式和静态代理模式很像,代码结构也几乎是一模一样的,它们的主要区别不在于代码结构,而在于应用的语境和语义。

设计模式的命名规则都是来源于生活场景,懂得在不同场景下应用不同的设计模式思想,比研究设计模式的代码具体要怎么写更有意义。

抛开具体的编程语言,设计模式的写法应该是千变万化的。

但是针对一种具体的编程语言,设计模式总会有一些流行的常规写法。

继续阅读