Before model learning
What is the design model: when we design the program, we gradually formed some typical problems and solutions, which is the software model; each mode describes a problem that often occurs in our program design and the solution to the problem. When we encounter the problem described by the mode, the corresponding solution can be used to solve the problem. This is the design mode.
A design pattern is an abstract thing. It is not learned but used. Maybe you don't know any pattern at all, and you don't consider any pattern, but write the best code, even from the perspective of "pattern experts", they are all the best designs and have to say "best pattern practices". This is because you have accumulated a lot of practical experience, knowing "where to write code" is a design pattern.
Some people say: "If the level is not reached, you can learn it in vain. If the level is reached, you can do it yourself ". It is true that the mode is well-developed and may still not be able to write good code, let alone design a good framework. OOP understanding and practical experience reach a certain level, and it also means to summarize a lot of good design experience, however, it may not be the case that "no teacher can do anything", or you can say that, on the basis of level and experience, it is time for the system to learn the "mode, it is helpful to learn the experts' summary and verify your own shortcomings.
This series of design patterns learning notes is actually a Learning Record for the book "Java and patterns.
Java I/O Library Design Principles
In the design of the Java I/O library, two structural modes are used, namely the decoration mode and the adapter mode. This article discusses the design of Java I/O libraries based on these two models.
Java library's two Symmetry
(1) Output-input symmetry: InputStream and OutputStream for processing Byte streams; Reader and Writer for processing Char streams.
(2) byte-char symmetry: InputStream and Reader sub-classes are responsible for the input of Byte and Char respectively; OutputStream and Writer sub-classes are responsible for the output of Byte and Char streams respectively, they form a parallel hierarchical structure.
Two Java library design modes
(1) Decoration mode: The most famous application of the decoration mode in Java language is the design of the Java I/O standard library.
(2) Adapter mode: the adapter mode is the second most important design mode in the Java I/O library.
Application of decoration Mode
Decoration mode in InputStream type
Structure:
Roles of the decoration mode:
(1) role of the abstract Component (Component): played by InputStream. This is an abstract class that provides a unified interface for various substream processors.
(2) ConcreteComponent role: played by ByteArrayInputStream, FileInputStream, PipedInputStream, StringBufferInputStream, and other original stream processors. They implement the interfaces defined by the abstract component role and can be decorated by the linked stream processor.
(3) Decorator role: played by FilterInputStream. It implements the interface specified by InputStream.
(4) ConcreteDecorator roles: these roles are assumed by several classes: DataInputStream, BufferInputStream, and LineNumberInputStream and PushBackInputStream.
Note: StringBufferInputStream and LineNumberInputStream are outdated and are no longer recommended.
Decoration mode in OutputStream type
Structure:
Roles of the decoration mode:
(1) role of the abstract Component (Component): played by OutputStream. This is an abstract class that provides a unified interface for various substream processors.
(2) The role of a specific component (ConcreteComponent) is played by ByteArrayOutputStream, FileOutputStream, and PipedOutputStream. They all implement the APIS declared by OutputStream.
(3) Decorator role: played by FilterOutputStream. It has the same interface as OutputStream, and this is the key to the decoration class.
(4) The ConcreteDecorator role is played by several classes: BufferedOutputStream, DataOutputStream, and PrintStream.
Decoration mode in Reader Type
Structure:
Roles of the decoration mode:
(1) role of the abstract Component (Component): played by Reader. This is an abstract class that provides a unified interface for various substream processors.
(2) specific component (ConcreteComponent) Roles: including CharArrayReader, InputStreamReader, PipedReader, and StringReader, all of which implement the interfaces declared by Reader.
(3) Decorator role: played by BufferedReader and FilterReader. The two have the same interface as Readeer, and this is the key to the decoration class.
(4) ConcreteD roles: LineNumberReader serves as the decoration role of BufferedReader, and PushbackReader serves as the decoration role of FilterReader.
Decoration mode in Writer type
Structure:
Roles of the decoration mode:
(1) role of the abstract Component (Component): played by Writer. This is an abstract class that provides a unified interface for various substream processors.
(2) The role of a specific component (ConcreteComponent) is played by CharArrayWriter, OutputStreamWriter, PipedWriter, and StringWriter. They all implement the interfaces declared by Reader.
(3) Decorator roles: played by BufferedWriter, FilterWriter, and PrintWriter. They have the same interfaces as Writer.
(4) ConcreteDecorator role: it is merged with the abstract decoration role. Since abstract decoration roles are merged with specific decoration roles, the decoration mode is simplified here.
Comparison between decoration mode and adapter Mode
(1) Decoration mode and adapter mode are designed by encapsulating other objects.
(2) The ideal decoration mode requires that the interface of the specific component role and decoration role be exactly the same as that of the abstract component role when the function of the decorated object is enhanced; the adapter mode is not. Generally, the adapter mode does not require enhancement of the functions of the source object. It only uses the functions of the source object, but changes the interfaces of the source object, to be consistent with the target interface.
(3) There are two decorative modes: transparent and translucent. the difference lies in whether the interfaces are completely consistent. The important fact about the decoration mode is that it is difficult to find the ideal decoration mode. In general, adding new behaviors will occur when the function of an object is enhanced. Therefore, it is difficult to avoid the interface width of the decorative role than that of the abstract component role, this phenomenon exists in many types of linked stream processors in the Java I/O library. The more new methods a decoration class provides, the farther it is from the pure decoration mode, and the closer it is from the adapter mode.
Application in adapter Mode
Adapter mode in InputStream original stream processor
ByteArrayInputStream is an adapter class:
FileInputStream is an adapter class:
StringBufferInputStream is an adapter class:
Adapter mode in OutputStream original stream processor
ByteArrayOutputStream is an adapter class:
FileOutputStream is an adapter class:
PipedOutputStream is an adapter class:
Adapter mode in Reader original stream processor
CharArrayReader is an adapter class:
StringReader is an adapter class:
Others, such as InputStreamReader and PipedReader, are also adapter classes.
Adapter mode in Writer type
CharArrayWriter is an adapter class:
PipedWriter is an adapter class:
StringWriter is an adapter class:
Java I/O sample code
Buffer input files
If you want to open a file for character input, you can use FileReader. To provide speed, we may want to buffer that file, so we will pass the generated reference to a BufferedReader object.
The sample code (BufferedInputFile. java) is as follows:
// Buffer the Input File import java. io. *; class BufferedInputFile {public static String read (String filename) throws IOException {// Reading input by linesBufferedReader in = new BufferedReader (new FileReader (filename); String s; stringBuilder sb = new StringBuilder (); // note that the StringBuilder used here is introduced by JDK5.0. The only difference between StringBuilder and StringBuffer is that it is not thread-safe, therefore, the performance is higher while (s = in. readLine ())! = Null) {sb. append (s + "\ n");} in. close (); return sb. toString ();} public static void main (String [] args) throws IOException {System. out. println (read ("BufferedInputFile. java "));}}
Input content from memory
Use StringReader to read characters, or use DataInputStream to read bytes. The Code is as follows:
// Input from memory: Use StringReader without Chinese garbled characters import java. io. *; class MemoryInput {public static void main (String [] args) throws IOException {StringReader in = new StringReader (BufferedInputFile. read ("MemoryInput. java "); int c; while (c = in. read ())! =-1) {System. out. print (char) c) ;}}// input from memory; Use DataInputStream, which is a byte-oriented I/O class (not character-oriented ), class FormattedMemoryInput {public static void main (String [] args) throws IOException {try {DataInputStream in = new DataInputStream (new ByteArrayInputStream (BufferedInputFile. read ("MemoryInput. java "). getBytes (); while (true) {System. out. print (char) in. readByte () ;}} catch (EOFException e) {System. err. println ("End Of Stream ") ;}}// input from memory; judge the end Of the file class TestEOF {public static void main (String [] args) throws IOException {DataInputStream in = new DataInputStream (new BufferedInputStream (new FileInputStream ("MemoryInput. java "); while (in. available ()! = 0) {System. out. print (char) in. readByte ());}}}
Basic file output
A FileWriter object can write data to a file. BufferedWriter is usually used to encapsulate the data to buffer the output (the buffer often significantly increases the performance of I/O operations ). In this example, It is decorated as PrintWriter to provide a Formatting Mechanism. Install the data created in this way and read it as plain text. The Code is as follows:
// Basic file output import java. io. *; class BasicFileOutput {public static void main (String [] args) throws IOException {BufferedReader in = new BufferedReader (new StringReader (BufferedInputFile. read ("BasicFileOutput. java "); // PrintWriter out = new PrintWriter (new BufferedWriter (new FileWriter (" BasicFileOutput. out "); // you can also use a simplified method, which is still in the process cache without having to implement it yourself. Unfortunately, there are no shortcuts for other common write tasks. Therefore, a typical I/O person contains a large number of redundant texts PrintWriter out = new PrintWriter ("BasicFileOutput. out "); int lineCount = 1; String s; while (s = in. readLine ())! = Null) {out. println (lineCount ++ ":" + s);} out. close (); // Show the stored file: System. out. println (BufferedInputFile. read ("BasicFileOutput. out "));}}
Store and restore Data
PrintWriter can format the data for reading. If you want to output data that can be restored by another "stream", you need to use DataOutputStream to write data and use DataInputStream to restore data. Of course, these streams can be in any form, but in the following example, a file is used and read/write is buffered. The Code is as follows:
// Store and restore Data import java. io. *; class StoringAndRecoveringData {public static void main (String [] args) throws IOException {DataOutputStream out = new DataOutputStream (new BufferedOutputStream (new FileOutputStream ("Data.txt"); out. writeDouble (3.14159); out. writeUTF ("hello"); out. writeDouble (1.111222); out. writeUTF ("'That's True"); out. close (); DataInputStream in = new DataInputStream (new BufferedInputStream (new FileInputStream ("Data.txt"); System. out. println (in. readDouble (); System. out. println (in. readUTF (); System. out. println (in. readDouble (); System. out. println (in. readUTF ());}}
Simple compression with GZIP
The GZIP interface is very simple. It may be suitable if we only want to compress a single data stream (instead of a series of different data streams. The Code is as follows:
// Use GZIP for simple compression import java. io. *; import java.util.zip. *; class GZIPcompress {public static void main (String [] args) throws IOException {if (args. length = 0) {System. out. println ("Usage: \ nGZIPcompress file \ nUses GZIP compression to compress the file to test.gz"); System. exit (1);} // write the compressed file into the disk BufferedReader in = new BufferedReader (new FileReader (args [0]); BufferedOutputStream out = new BufferedOutputStream (new G ZIPOutputStream (new FileOutputStream ("test.gz"); System. out. println ("Writing file"); int c; while (c = in. read ())! =-1) {out. write (c);} in. close (); out. close (); // read the compressed file System from the disk. out. println ("Reading file"); BufferedReader in2 = new BufferedReader (new InputStreamReader (new GZIPInputStream (new FileInputStream ("test.gz"); String s; while (s = in2.readLine ())! = Null) {System. out. println (s );}}}
Structure Pattern Summary
There are seven schema patterns: adapter, decoration, synthesis, proxy, metadata, facade, and bridge.
Summary: