美文网首页
结构性模式(二):装饰器、外观模式

结构性模式(二):装饰器、外观模式

作者: JBryan | 来源:发表于2020-03-18 15:04 被阅读0次

1、装饰器模式(Decorator Pattern)

装饰器模式主要解决继承关系过于复杂的问题,通过组合来替代继承。它主要的作用是给原始类添加增强功能。这也是判断是否该用装饰器模式的一个重要的依据。除此之外,装饰器模式还有一个特点,那就是可以对原始类嵌套使用多个装饰器。为了满足这个应用场景,在设计的时候,装饰器类需要跟原始类继承相同的抽象类或者接口。

1.1、实例说明

现有一套图形界面显示构件库,在使用该库构建某图形界面时,用户要求为界面定制一些特效显示效果,如带滚动条的窗体或透明窗体等。

1.2、实例类图
装饰器.jpg
1.3、实例代码
public abstract class Window {
    protected abstract void display();
}

public class SimpleWindow extends Window {
    @Override
    protected void display() {
        System.out.println("显示简单窗体");
    }
}

public class WindowDecorator extends Window {

    Window window;

    WindowDecorator(Window window){
        this.window = window;
    }

    @Override
    protected void display() {
        window.display();
    }
}

public class ScrollbarDecorator extends WindowDecorator {
    ScrollbarDecorator(Window window) {
        super(window);
    }

    @Override
    protected void display() {
        super.display();
        this.setScrollbar();
    }

    public void setScrollbar(){
        System.out.println("装饰器增强滚动条功能。。。。");
    }
}

public class TransparentDecorator extends WindowDecorator {
    TransparentDecorator(Window window) {
        super(window);
    }

    @Override
    protected void display() {
        super.display();
        this.setTransparent();
    }

    public void setTransparent(){
        System.out.println("装饰器增强透明窗体功能。。。。");
    }
}

public class Client {
    public static void main(String[] args) {
        Window simpleWindow = new SimpleWindow();
        Window scrollbarWindow = new ScrollbarDecorator(simpleWindow);
        Window transparentWindow = new TransparentDecorator(scrollbarWindow);
        transparentWindow.display();
    }
}

1.4、装饰器模式在Java IO类库中的使用

在Java IO类库中,经常使用类似下面的代码:

InputStream in = new FileInputStream("C:\\upload_file\\IOTest.txt");
InputStream bin = new BufferedInputStream(in);
DataInputStream din = new DataInputStream(bin);
int data = din.readInt();

现在简单分析下装饰器模式在上面代码中的应用
类图:


IO装饰模式.jpg

抽取InputStream、BufferedInputStream和FileInputStream部分源码如下:

public abstract class InputStream implements Closeable {
    public int read(byte b[]) throws IOException {
        return read(b, 0, b.length);
    }
}

public class FileInputStream extends InputStream{
  public FileInputStream(String name) throws FileNotFoundException {
        this(name != null ? new File(name) : null);
    }
  public int read(byte b[], int off, int len) throws IOException {
        return readBytes(b, off, len);
    }
   private native int readBytes(byte b[], int off, int len) throws IOException;
}

public class FilterInputStream extends InputStream {
    /**
     * The input stream to be filtered.
     */
    protected volatile InputStream in;
    protected FilterInputStream(InputStream in) {
        this.in = in;
    }
    public int read(byte b[], int off, int len) throws IOException {
        return in.read(b, off, len);
    }

}

public class BufferedInputStream extends FilterInputStream {
    private static int DEFAULT_BUFFER_SIZE = 8192;
    public BufferedInputStream(InputStream in) {
        this(in, DEFAULT_BUFFER_SIZE);
    }
    public BufferedInputStream(InputStream in, int size) {
        super(in);
        if (size <= 0) {
            throw new IllegalArgumentException("Buffer size <= 0");
        }
        buf = new byte[size];
    }
    public synchronized int read(byte b[], int off, int len) throws IOException{
        //省略其他关于缓存功能的代码
        in.read(buffer, pos, buffer.length - pos);
        //省略其他关于缓存功能的代码
    }
}

public class DataInputStream extends FilterInputStream implements DataInput {
  public DataInputStream(InputStream in) {
        super(in);
    }
  public final int readInt() throws IOException {
        int ch1 = in.read();
        int ch2 = in.read();
        int ch3 = in.read();
        int ch4 = in.read();
        //在in.read()基础上,添加读取int功能
        if ((ch1 | ch2 | ch3 | ch4) < 0)
            throw new EOFException();
        return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4 << 0));
    }
}

2、外观模式(Facade Pattern)

为了保证接口的可复用性(或者叫通用性),我们需要将接口尽量设计得细粒度一点,职责单一一点。但是,如果接口的粒度过小,在接口的使用者开发一个业务功能时,就会导致需要调用 n 多细粒度的接口才能完成。
相反,如果接口粒度设计得太大,一个接口返回 n 多数据,要做 n 多事情,就会导致接口不够通用、可复用性不好。接口不可复用,那针对不同的调用者的业务需求,我们就需要开发不同的接口来满足,这就会导致系统的接口无限膨胀。
外观模式为子系统提供一组统一的接口,定义一组高层接口让子系统更易用。

2.1、实例说明

需要一个文件加密模块,加密流程包括三个操作,分别是读取源文件、加密、保存加密之后的文件。读取文件和保存文件使用流来实现,这三个操作相对独立,其业务代码封装在三个不同的类中。现在需要提供一个统一的加密外观类,用户可以直接使用该加密外观类完成文件的读取、加密、和保存三个操作,而不需要与每一个类进行交互。

2.2、实例类图
外观模式.jpg
2.3、实例代码
public class FileReader {

    public String read(String srcFile){
        //省略读文件代码
        return "";
    }
}

public class FileWriter {
    public void write(String txt,String destFile){
        //省略写文件代码
    }
}

public class CipherMachine {
    public String encrypt(String txt){
        //省略加密代码
        return "";
    }
}

public class EncryptFacade {
    FileReader reader;
    FileWriter writer;
    CipherMachine cipherMachine;
    EncryptFacade(){
        this.reader = new FileReader();
        this.writer = new FileWriter();
        this.cipherMachine = new CipherMachine();
    }

    public void fileEncrypt(String srcFile,String destFile){
        String txt = reader.read(srcFile);
        String txtEncrypt = cipherMachine.encrypt(txt);
        writer.write(txtEncrypt,destFile);
    }
}

public class Client {
    public static void main(String[] args) {
        EncryptFacade ef = new EncryptFacade();
        ef.fileEncrypt("C:\\upload_file\\IOTest.txt","C:\\upload_file\\IOTest.txt");
    }
}

适配器模式和外观模式的共同点是,将不好用的接口适配成好用的接口。它们的区别:
适配器是做接口转换,将不同的接口转化为统一的接口,解决的是原接口和目标接口不匹配的问题。
外观模式做接口整合,将不同的接口整合为一个接口,解决的是多接口调用带来的问题。

相关文章

网友评论

      本文标题:结构性模式(二):装饰器、外观模式

      本文链接:https://www.haomeiwen.com/subject/axfqyhtx.html