装饰器模式与Java IO

装饰器模式

装饰器模式是一种类继承的替代方案,用于以客户端透明的方式在基类上增加各种新的功能。相比于继承,使用装饰器模式可以显著减少类的个数。
以下以绘图形状为例展示装饰器模式。装饰器模式的基础是形状接口Shape.

public interface Shape {
    public void draw();
}

核心是实现了Shape接口并以Shape对象作为属性的ShapeDecorator装饰器基类。装饰器模式的关键就在于装饰器基类同时包含接口对象和继承接口。(可以思考为什么装饰器基类要同时使用包含继承

public class ShapeDecorator implements Shape {
    protected Shape shape;

    public ShapeDecorator(Shape shape) {
        this.shape = shape;
    }

    @Override
    public void draw() {
        shape.draw();
    }
}

假设我们用装饰器模式来装饰一个矩形形状Rectangle

public class Rectangle implements Shape {
    @Override
    public void draw() {
        System.out.println("Rectangle");
    }
}

分别使用红色填充装饰器RedColorDecorator

public class RedColorDecorator extends ShapeDecorator {

    public RedColorDecorator(Shape shape) {
        super(shape);
    }

    private void setRedColor() {
        System.out.println("Color == Red");
    }

    @Override
    public void draw() {
        super.draw();
        setRedColor();
    }
    
}

和蓝色边框装饰器BlueBorderDecorator

public class BlueBorderDecorator extends ShapeDecorator {

    public BlueBorderDecorator(Shape shape) {
        super(shape);
    }

    private void setBlueBorder() {
        System.out.println("Border == Blue");
    }

    @Override
    public void draw() {
        super.draw();
        setBlueBorder();
    }

}

最后,我们在客户端(驱动类Main)中装饰矩形的填充和边框。

public class Main {
    public static void main(String[] args) {
        Shape shape = new Rectangle();
        shape.draw();
        System.out.println("--------------------");
        shape = new RedColorDecorator(shape);
        shape.draw();
        System.out.println("--------------------");
        shape = new BlueBorderDecorator(shape);
        shape.draw();
        System.out.println("--------------------");
        // stdout
        // Rectangle
        // --------------------
        // Rectangle
        // Color == Red
        // --------------------
        // Rectangle
        // Color == Red
        // Border == Blue
        // --------------------
    }
}

如果我们要使用很多形状、很多填充、很多边框,那么,相比继承,使用装饰器模式可以大大减少类的个数。例如,我们使用矩形、圆形、三角形这3种形状,使用红、黄、蓝、绿这4种颜色的填充和边框,则使用继承的方式,除了基类外,要创建3*4*4=48个具体的类,类的数量面临组合爆炸问题;而使用装饰器模式,除了基类外,只需要创建3+4+4=11个具体的类(3个形状类,4个填充类,4个边框类),类的个数从乘法变成了加法,而且形状类、填充类、边框类各自的行为被抽离出来,便于统一管理。

java.io中的装饰器模式

java.io包中大量使用装饰器模式。下面以字节输入流为例,主要涉及InputStream, FilterInputStream, FileInputStream, BufferedInputStream类。 jdk版本11.0.4.
InputStream是抽象基类,相当于上面例子中的Shape接口。

/**
 * This abstract class is the superclass of all classes representing
 * an input stream of bytes.
 *
 * <p> Applications that need to define a subclass of <code>InputStream</code>
 * must always provide a method that returns the next byte of input.
 *
 * @author  Arthur van Hoff
 * @see     java.io.BufferedInputStream
 * @see     java.io.ByteArrayInputStream
 * @see     java.io.DataInputStream
 * @see     java.io.FilterInputStream
 * @see     java.io.InputStream#read()
 * @see     java.io.OutputStream
 * @see     java.io.PushbackInputStream
 * @since   1.0
 */
public abstract class InputStream implements Closeable

FileInputStream类是InputStream类的针对文件输入的一个具体实现,相当于上面的Rectangle类。

/**
 * A <code>FileInputStream</code> obtains input bytes
 * from a file in a file system. What files
 * are  available depends on the host environment.
 *
 * <p><code>FileInputStream</code> is meant for reading streams of raw bytes
 * such as image data. For reading streams of characters, consider using
 * <code>FileReader</code>.
 *
 * @apiNote
 * To release resources used by this stream {@link #close} should be called
 * directly or by try-with-resources. Subclasses are responsible for the cleanup
 * of resources acquired by the subclass.
 * Subclasses that override {@link #finalize} in order to perform cleanup
 * should be modified to use alternative cleanup mechanisms such as
 * {@link java.lang.ref.Cleaner} and remove the overriding {@code finalize} method.
 *
 * @implSpec
 * If this FileInputStream has been subclassed and the {@link #close}
 * method has been overridden, the {@link #close} method will be
 * called when the FileInputStream is unreachable.
 * Otherwise, it is implementation specific how the resource cleanup described in
 * {@link #close} is performed.

 *
 * @author  Arthur van Hoff
 * @see     java.io.File
 * @see     java.io.FileDescriptor
 * @see     java.io.FileOutputStream
 * @see     java.nio.file.Files#newInputStream
 * @since   1.0
 */
public class FileInputStream extends InputStream

FilterInputStream相当于上面例子中的ShapeDecorator类,是装饰器基类,继承了InputStream且包含InputStream对象。

/**
 * A <code>FilterInputStream</code> contains
 * some other input stream, which it uses as
 * its  basic source of data, possibly transforming
 * the data along the way or providing  additional
 * functionality. The class <code>FilterInputStream</code>
 * itself simply overrides all  methods of
 * <code>InputStream</code> with versions that
 * pass all requests to the contained  input
 * stream. Subclasses of <code>FilterInputStream</code>
 * may further override some of  these methods
 * and may also provide additional methods
 * and fields.
 *
 * @author  Jonathan Payne
 * @since   1.0
 */
public
class FilterInputStream extends InputStream {
    /**
     * The input stream to be filtered.
     */
    protected volatile InputStream in;

    /**
     * Creates a <code>FilterInputStream</code>
     * by assigning the  argument <code>in</code>
     * to the field <code>this.in</code> so as
     * to remember it for later use.
     *
     * @param   in   the underlying input stream, or <code>null</code> if
     *          this instance is to be created without an underlying stream.
     */
    protected FilterInputStream(InputStream in) {
        this.in = in;
    }
	
	...
}

BufferedInputStream类进一步继承了FilterInputStream类,实现了具体的带缓冲区的字节流输入功能,相当于上面例子中的RedColorDecoratorBlueBorderDecorator类。

/**
 * A <code>BufferedInputStream</code> adds
 * functionality to another input stream-namely,
 * the ability to buffer the input and to
 * support the <code>mark</code> and <code>reset</code>
 * methods. When  the <code>BufferedInputStream</code>
 * is created, an internal buffer array is
 * created. As bytes  from the stream are read
 * or skipped, the internal buffer is refilled
 * as necessary  from the contained input stream,
 * many bytes at a time. The <code>mark</code>
 * operation  remembers a point in the input
 * stream and the <code>reset</code> operation
 * causes all the  bytes read since the most
 * recent <code>mark</code> operation to be
 * reread before new bytes are  taken from
 * the contained input stream.
 *
 * @author  Arthur van Hoff
 * @since   1.0
 */
public
class BufferedInputStream extends FilterInputStream

最终形成的继承树中,InputStream是字节流输入基类,ByteArrayInputStreamFileInputStream继承InputStream,分别实现从字节数组的输入和文件的输入。FilterInputStream也继承InputStream,同时包含InputStream对象,作为装饰器基类。BufferedInputStream, DataInputStream, PushBackInputStream继承FilterInputStream,分别提供缓冲区、解析到Java基本类型和回写到流的装饰功能。因此,我们在写缓冲区的文件字节流读取时,经常采用下面的语句:

BufferedInputStream bis = new BufferedInputStream(new FileInputStream(filename));
发布了708 篇原创文章 · 获赞 140 · 访问量 24万+

猜你喜欢

转载自blog.csdn.net/da_kao_la/article/details/104113186
今日推荐