Decoration mode WrapperMode

Decoration mode

Decoration mode, also known as Decorator or Wrapper mode

Refer to Alexander Shvets' in-depth design pattern

1. Purpose of decoration mode

Decorator pattern is a very common design pattern. Decorator pattern can dynamically add other responsibilities to an object. The decorator pattern is more flexible than subclassing in terms of extending object functionality

  • Inheritance is static. You cannot change the behavior of an existing object at run time. You can only replace the current entire object with an object created by a different subclass.
  • A subclass can only have one parent. Most programming languages do not allow a class to inherit the behavior of multiple classes at the same time.

When you need to generate many subclasses through inheritance for enhancement, you can use decoration mode

Try to analyze a wave

If you write a notification function, there is an implementation

Now we want to expand or enhance the function of notification. The first feeling is to write more subclasses (or implementation classes)

But if you can use, set a Wrapper or Decorator for each function

It can be like this

Or do you want to design a very awesome Wrapper that can complete all functions

Obviously, enhancing the functionality of a class (or interface) through the decorator pattern is more flexible than inheriting

2. Ideas of decoration mode

  1. Ensure that business logic can be represented by one basic component and multiple additional optional layers.

  2. A common way to find basic components and optional hierarchies. Create a component interface and declare these methods in it.

  3. Create a concrete component class and define its underlying behavior.

  4. Create a decoration base class and use a member variable to store a reference to the encapsulated object. The member variable must be declared as a component interface type to connect specific components and decorations at run time. The decoration base class must delegate all work to the encapsulated object. (implement the same interface as the proxy mode)

  5. Ensure that all classes implement component interfaces.

  6. Extend the decoration base class to a concrete decoration. The concrete decoration must perform its own behavior before or after calling the parent method (always delegated to the encapsulated object). (perform enhanced operation)

  7. The client code is responsible for creating decorations and combining them into the form required by the client. (select the decorator Wrapper of the required function and inject the specific component into it. The enhanced method or function can be called through the Wrapper)

[the external link image transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the image and upload it directly (img-pcdljtko-1631076356634) (F: \ localtypropictrue \ know. PNG)]

3. Code implementation

1. Basic interface

/**
 * 
 * @author ASUS     
 * 	Interface to be decorated
 *  Data source interface
 * 	Provides methods for reading and writing data
 */
public interface DataSource {
	
	/**
	 * Read data
	 * @throws FileNotFoundException There is no exception in the file
	 * @throws IOException 
	 */
	void ReaderData() throws  IOException;
	
	/**
	 * Write data
	 * @throws FileNotFoundException 
	 * @throws IOException 
	 */
	void WriteData(String filename,String Context) throws IOException;

}

Specific components

/**
 * 
 * @author ASUS
 *     Implementation class of data source interface
 *     File data source
 *     Realize the reading and writing of data in the file
 */
public class FileDataSource implements DataSource{
	
	/**
	 * file name
	 */
	private String filename;
	
	/**
	 * Constructor specifies the data source file name
	 * @param filename
	 */
	public FileDataSource(String filename) {
		this.filename = filename;
	}

	@Override
	public void ReaderData() throws IOException {
		FileInputStream in = new FileInputStream(new File(filename));
		DataInputStream din = new DataInputStream(in);
		String Context = din.readUTF();
//		byte[] bytes = new byte[1024];
//		int len = 0;
//		while ((len = in.read(bytes))!=-1) {
//			System.out.println(new String(bytes, 0, len));
//		}
		System.out.println(Context);
		in.close();
	}

	@Override
	public void WriteData(String filename,String Context) throws IOException {
		FileOutputStream out = new FileOutputStream(new File(filename),true);
		DataOutputStream dout = new DataOutputStream(out);
		dout.writeUTF(Context);
		dout.close();
		out.close();
	}
	
	

}

Basic packaging:

/**
 *      Data source decoration class
 * @author ASUS
 *     It implements the same basic interface as the specific component, and combines an instance of the interface to let the associated instance execute the operation method, which is the same as the proxy module
 */
public class DataSourceWrapper implements DataSource {
	
	/**
	 * Combination (or associating an instance of an enhanced interface)
	 */
	private DataSource datasource;
	
	/**
	 *     Inject a DataSource through the construction method
	 * @param daSource
	 */
	public DataSourceWrapper(DataSource datasource) {
		this.datasource = datasource;
	}

    //The basic decoration class can not enhance the datasource object,
    //Instead, it is enhanced in specific decoration classes by associating object execution methods
	@Override
	public void ReaderData() throws IOException {
		datasource.ReaderData();
	}

	@Override
	public void WriteData(String filename, String Context) throws IOException {
		datasource.WriteData(filename, Context);
	}
	
}

Specific packaging:

/**
 *       Enhanced packaging
 *  @author ASUS
 * 	By inheriting the base wrapper class and adding enhanced functionality to it
 */
public class EncrptionDataSourceWrapper extends DataSourceWrapper{

	public EncrptionDataSourceWrapper(DataSource daSource) {
		super(daSource);
	}

	@Override
	public void ReaderData() throws IOException {
		// TODO Auto-generated method stub
		super.ReaderData();
	}

	@Override
	public void WriteData(String filename, String Context) throws IOException {
		// TODO Auto-generated method stub
		//Call the enhancement method to enhance the interface function
		super.WriteData(filename, EncoderContext(Context));
	}
	
	/**
	 * 
	 * @param Enhanced methods for interfaces
	 * @return Encrypt data
	 */
	public String EncoderContext(String Context) {
		Encoder encoder = Base64.getEncoder();
		return encoder.encodeToString(Context.getBytes());
	}

}

Specific packaging:

/**
 * Specific decorator category
 * @author ASUS
 * Enhancements: compression
 * Inherited from base decorator class
 * DataSourceWrapper
 */
public class CompressDataSourceWrapper extends DataSourceWrapper{

	//Inject the specific implementation class of the composition to call the basic method
	public CompressDataSourceWrapper(DataSource datasource) {
		super(datasource);
	}

	@Override
	public void ReaderData() throws IOException {
		super.ReaderData();
	}

	@Override
	public void WriteData(String filename, String Context) throws IOException {
        super.WriteData(filename,Context);
		doZip(new File(filename), new ZipOutputStream(new FileOutputStream(new File("shit.zip"))),
              "dir",Context);
	}
	
    /**
     Provide enhanced methods for interfaces
     @param: File infile Destination file name in the compressed package
     @param: ZipOutputStream out Compressed output stream
     @param: String dir Compressed file directory name
     @param: String Context Compress the contents of the target file
    */
	public void doZip(File outFile, ZipOutputStream out, String dir,String Context) throws IOException {
        String entryName = null;
        if (!"".equals(dir)) {
            entryName = dir + "/" + outFile.getName();
        } else {
            entryName = outFile.getName();
        }
        ZipEntry entry = new ZipEntry(entryName);
        out.putNextEntry(entry);
        DataOutputStream dout = new DataOutputStream(out);
        dout.writeUTF(Context);
        
        out.closeEntry();
        dout.close();
    }
    

}

Client code:

/**
 * Test decoration mode
 * @author ASUS
 *
 */
public class AppMain {
	public static void main(String[] args) {
        
        //Create a concrete component object
		DataSource source = new FileDataSource("fuck.txt");
		
        //Create concrete wrapper classes and inject concrete components
		DataSourceWrapper wrapper = new EncrptionDataSourceWrapper(source);
		DataSourceWrapper wrapper2 = new CompressDataSourceWrapper(source);
		
		try {
			//wrapper.WriteData("sure.txt", "see if it's over with ciphertext");
            //The enhanced method is implemented through the wrapper class
			wrapper2.WriteData("sure.txt", "Refresh the project to see if there are compressed packages?");
			
		} catch (IOException e) {
			
			e.printStackTrace();
		}
	}

}

Like the proxy model, it uses the idea of composition to hand over the execution of specific methods to the combined objects, and then enhance the methods in specific decoration classes

4. Advantages and disadvantages of decoration mode

  • You can extend the behavior of an object without creating a new subclass.

  • You can add or remove objects at run time.

  • You can combine several behaviors by encapsulating objects with multiple decorations.

  • Single responsibility principle. You can split a large class that implements many different behaviors into several smaller classes.

  • It is difficult to remove a specific wrapper from the wrapper stack.

  • It is difficult to implement decoration whose behavior is not affected by the order of decoration stack.

  • The initialization configuration code for each layer may look bad.

5. Catch wind and shadow

In the IO stream, the code of decoration mode is very obvious,

/**
 * This class is the superclass of all classes that filter output
 * streams. These streams sit on top of an already existing output
 * stream (the <i>underlying</i> output stream) which it uses as its
 * basic sink of data, but possibly transforming the data along the
 * way or providing additional functionality.
 * <p>
 * The class <code>FilterOutputStream</code> itself simply overrides
 * all methods of <code>OutputStream</code> with versions that pass
 * all requests to the underlying output stream. Subclasses of
 * <code>FilterOutputStream</code> may further override some of these
 * methods as well as provide additional methods and fields.
 *
 * @author  Jonathan Payne
 * @since   JDK1.0
 */
public
class FilterOutputStream extends OutputStream {
    /**
     * The underlying output stream to be filtered.
     */
    protected OutputStream out;

    /**
     * Creates an output stream filter built on top of the specified
     * underlying output stream.
     *
     * @param   out   the underlying output stream to be assigned to
     *                the field <tt>this.out</tt> for later use, or
     *                <code>null</code> if this instance is to be
     *                created without an underlying stream.
     */
    public FilterOutputStream(OutputStream out) {
        this.out = out;
    }

    /**
     * Writes the specified <code>byte</code> to this output stream.
     * <p>
     * The <code>write</code> method of <code>FilterOutputStream</code>
     * calls the <code>write</code> method of its underlying output stream,
     * that is, it performs <tt>out.write(b)</tt>.
     * <p>
     * Implements the abstract <tt>write</tt> method of <tt>OutputStream</tt>.
     *
     * @param      b   the <code>byte</code>.
     * @exception  IOException  if an I/O error occurs.
     */
    public void write(int b) throws IOException {
        out.write(b);
    }

    /**
     * Writes <code>b.length</code> bytes to this output stream.
     * <p>
     * The <code>write</code> method of <code>FilterOutputStream</code>
     * calls its <code>write</code> method of three arguments with the
     * arguments <code>b</code>, <code>0</code>, and
     * <code>b.length</code>.
     * <p>
     * Note that this method does not call the one-argument
     * <code>write</code> method of its underlying stream with the single
     * argument <code>b</code>.
     *
     * @param      b   the data to be written.
     * @exception  IOException  if an I/O error occurs.
     * @see        java.io.FilterOutputStream#write(byte[], int, int)
     */
    public void write(byte b[]) throws IOException {
        write(b, 0, b.length);
    }

    /**
     * Writes <code>len</code> bytes from the specified
     * <code>byte</code> array starting at offset <code>off</code> to
     * this output stream.
     * <p>
     * The <code>write</code> method of <code>FilterOutputStream</code>
     * calls the <code>write</code> method of one argument on each
     * <code>byte</code> to output.
     * <p>
     * Note that this method does not call the <code>write</code> method
     * of its underlying input stream with the same arguments. Subclasses
     * of <code>FilterOutputStream</code> should provide a more efficient
     * implementation of this method.
     *
     * @param      b     the data.
     * @param      off   the start offset in the data.
     * @param      len   the number of bytes to write.
     * @exception  IOException  if an I/O error occurs.
     * @see        java.io.FilterOutputStream#write(int)
     */
    public void write(byte b[], int off, int len) throws IOException {
        if ((off | len | (b.length - (len + off)) | (off + len)) < 0)
            throw new IndexOutOfBoundsException();

        for (int i = 0 ; i < len ; i++) {
            write(b[off + i]);
        }
    }

    /**
     * Flushes this output stream and forces any buffered output bytes
     * to be written out to the stream.
     * <p>
     * The <code>flush</code> method of <code>FilterOutputStream</code>
     * calls the <code>flush</code> method of its underlying output stream.
     *
     * @exception  IOException  if an I/O error occurs.
     * @see        java.io.FilterOutputStream#out
     */
    public void flush() throws IOException {
        out.flush();
    }

    /**
     * Closes this output stream and releases any system resources
     * associated with the stream.
     * <p>
     * The <code>close</code> method of <code>FilterOutputStream</code>
     * calls its <code>flush</code> method, and then calls the
     * <code>close</code> method of its underlying output stream.
     *
     * @exception  IOException  if an I/O error occurs.
     * @see        java.io.FilterOutputStream#flush()
     * @see        java.io.FilterOutputStream#out
     */
    @SuppressWarnings("try")
    public void close() throws IOException {
        try (OutputStream ostream = out) {
            flush();
        }
    }
}

Source code of OutputStreamWriter

public class OutputStreamWriter extends Writer {

    private final StreamEncoder se;

    /**
     * Creates an OutputStreamWriter that uses the named charset.
     *
     * @param  out
     *         An OutputStream
     *
     * @param  charsetName
     *         The name of a supported
     *         {@link java.nio.charset.Charset charset}
     *
     * @exception  UnsupportedEncodingException
     *             If the named encoding is not supported
     */
    public OutputStreamWriter(OutputStream out, String charsetName)
        throws UnsupportedEncodingException
    {
        super(out);
        if (charsetName == null)
            throw new NullPointerException("charsetName");
        se = StreamEncoder.forOutputStreamWriter(out, this, charsetName);
    }

An instance of the output stream is also wrapped in the result of StreamEncoder

public class StreamEncoder extends Writer {
    private static final int DEFAULT_BYTE_BUFFER_SIZE = 8192;
    private volatile boolean isOpen;
    private Charset cs;
    private CharsetEncoder encoder;
    private ByteBuffer bb;
    //An output stream instance is also wrapped in it
    private final OutputStream out;
    private WritableByteChannel ch;
    private boolean haveLeftoverChar;
    private char leftoverChar;
    private CharBuffer lcb;
}

6. Reflection

In the decoration mode, an enhanced function is like a dress, which is sleeved on the original object to realize a function.

Put it on when you need to expand the function, and take it off when you don't need it

Like many objects in an io stream, you need to inject another stream object into the io stream

The enhancement method is added on the basis of calling its original method to complete the decoration

Transfer the extension function from adding subclasses to adding wrappers

Tags: Java Design Pattern UML

Posted on Tue, 23 Nov 2021 20:46:14 -0500 by Mr Camouflage