Another Example: Input/Output

A common use for the Decorator pattern is in stream I/O. Let's look at stream I/O a little before seeing how the pattern can be used here. I will limit the discussion to input since output works in an analogous way (if you see how it works in one direction, it should be clear how it works in the other direction). For any particular stream input, there is exactly one source, but there can be any number (including zero) of actions to perform on the input stream. For example, I can read from

  • A file

  • A socket and then decrypt the incoming stream

  • A file and then decompress the incoming data

  • A string

  • A file, decompress the input, and then decrypt it

Depending upon how the data were sent (or stored) any combination of behaviors is possible. Think of it this way: Any source can be decorated with any combination of behaviors. Some of the possibilities available for stream input are shown in Table 15-1.

Developers in object-oriented languages can take advantage of this by having source and behavior objects derive from a common abstract class. Each behavior object can be given its source or prior behavior in its constructor. A chain of actions is then built as the objects themselves are instantiated (each is given a reference to its trailing object). The sources derive from ConcreteComponent (see Figure 15-4), while the behaviors are decorators. Note that ConcreteComponent is now a misnomer because it is now abstract.

Table 15-1. Kinds of Sources and Behaviors
Sources Behaviors
String Buffered input
File Run checksum
Socket (TCP/IP) Unzip
Serial port Decrypt (any number of ways)
Parallel port Selection filters (any number of ways)
Keyboard  

For example, to get the behavior “read from a file, decompress the input, and then decrypt it,” do the following:

1.
Build the decorator chain by doing the following:

a. Instantiate a file object.

b. Pass a reference to it to the constructor of a decompression object.

c. Pass a reference to that to the constructor of a decryption object.

2.
Read, decompress, and decrypt the data—all transparently to the client object that is using it. The client merely knows it has some sort of input stream object.

If this client needs to get its input from a different source, the chain is created by instantiating a different source object using the same behavior objects.

Understanding “The Complete Stream Zoo.”[1]

Java is notorious for a confusing array of stream inputs and associated classes. It is much easier to understand these classes in the context of the Decorator pattern. The classes directly derived from java.io.InputStream (ByteArrayInputStream, FileInputStream, FilterInputStream, InputStream, ObjectInputStream, SequenceInputStream, and StringBufferInputStream) all play the role of the decorated object. All of the decorators derive from the FilterInputStream (either directly or indirectly).

Keeping the Decorator pattern in mind explains why the Java language requires chaining these objects together when they are instantiated—this gives programmers the ability to pick any number of combinations from the different behaviors available.


[1] Horstmann, C., Core Java—Volume 1—Fundamentals, Palo Alto, CA: Pearson Education, 1999, p. 627.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.225.117.253