1.07k likes | 1.22k Views
Unit 13 Decorator. Summary prepared by Kirk Scott. Design Patterns in Java Chapter 27 Decorator. Summary prepared by Kirk Scott. The Introduction Before the Introduction. Suppose you have a set of functionalities where you would like to mix and match the functionalities together
E N D
Unit 13Decorator Summary prepared by Kirk Scott
Design Patterns in JavaChapter 27Decorator Summary prepared by Kirk Scott
The Introduction Before the Introduction • Suppose you have a set of functionalities where you would like to mix and match the functionalities together • You would like to be able to create objects with various functionalities • This can be accomplished by repeated, wrapped construction (nested construction)
Let one object be the result of one construction sequence • Calling a given method on that object results in one subset of functionalities • Let another object be the result of another construction sequence • Calling the same method on the other object results in a different subset of functionalities
This chapter uses streams and writers from the Java API as the first example of the decorator pattern • It uses mathematical functions as the second example • This set of overheads will only cover the first example from the book • It will present another example that does not come from the book
The book previews the definition of the pattern with observations along these lines: • Extending a code base usually means adding classes to a design or methods to classes • The Decorator design pattern supports extension by allowing a program at run time to construct objects that can have varying behavior
Book Definition of Pattern • Book definition: • The intent of Decorator is to let you compose new variations of an operation at runtime.
A Classic Example: Streams and Writers • The Java API includes a set of classes that are related in such a way that they illustrate the Decorator design pattern • These are the stream and writer classes which are used for file I/O and other purposes • Let a generic FileReader or FileWriter be constructed in a program • It is connected to an external file
It is possible to pass the generic reader or writer as a construction parameter when making a more specific kind of reader or writer • The new reader or writer adds I/O functionalities that don’t exist in the generic reader or writer • The book illustrates this idea in the code on the following overhead
public class ShowDecorator • { • public static void main(String[] args) throws IOException • { • FileWriter file = new FileWriter("sample.txt"); • BufferedWriter writer = new BufferedWriter(file); • writer.write("a small amount of sample text"); • writer.newLine(); • writer.close(); • } • }
The book gives the foregoing example simply to illustrate the process of construction • It doesn’t comment specifically on the methods • The FileWriter class and the BufferedWriter classes both have various write() methods that take various sets of parameters
The decorator pattern opens up the possibility of overloading • The BufferedWriter write() method may take different parameters from the write() method in the FileWriter class • More importantly, calling write() on a BufferedWriter object may give different results from calling write() on a FileWriter object
The PrintWriter class may be a slightly better illustration • An instance is constructed the same way as the book’s BufferedWriter example • FileWriter file = new FileWriter("sample.txt"); • PrintWriter writer = new PrintWriter(file); • The PrintWriter class has additional methods besides write(), including print() and println()
From a print writer you gain the ability to write text to a file using the same method calls that you use to put it on the screen • This is the third thing you can get by using the pattern • You can add new methods to the class with different functionality
Using the decorator pattern, programmers can write their own file I/O classes that build on the API classes • The book’s next example builds a hierarchy of classes that make it possible to format text before writing it to a file • The formatting will be simple things like making it upper case or lower case
The book refers to these formatting classes as filter classes • It begins the presentation of the topic with the UML diagram given on the next overhead • This will require a little explanation, which is given afterwards
Both the Writer and FilterWriter abstract classes exist in the Java API • FilterWriter extends Writer • FilterWriter also contains an instance of Writer • This illustrates the basic plan • A FilterWriter wraps an instance of its superclass, adding functionality that doesn’t exist in the superclass
It is worth noting that structurally, this was the official design of the proxy pattern • In the proxy, the decision was ultimately made that a proxy in spirit was better • That meant just implementing a subclass without wrapping an instance of the superclass • Before the discussion of decorator is over with, it might be interesting to consider that alternative
The Java API textual documentation for the FilterWriter class is given on the following overhead • When you read this documentation you realize that the API is preparing you to apply the Decorator design pattern if you want to
Java API Documentation of the Class FilterWriter • Abstract class for writing filtered character streams. • The abstract class FilterWriter itself provides default methods that pass all requests to the contained stream. • Subclasses of FilterWriter should override some of these methods and may also provide additional methods and fields.
The initial diagram for the book’s example is repeated on the next overhead • Because the OozinozFilter class is a subclass of the FileWriter class, it will contain an instance variable of type Writer • In other words, the decorator structure is inherent in the Java API classes • the Java developers have decided this is the right structure • The user just extends the FilterWriter class and works with the wrapped Writer
Next, the book extends its example UML diagram • It is shown on the overhead following the next one • The diagram emphasizes that the OozinozFilter class contains a Writer (by inheritance from FilterWriter) by drawing the line directly from OozinozFilter to Writer
The OozinozFilter class is abstract • The diagram also shows its concrete subclasses which will be the actual filters in the example • Note again that the decorator structure of a subclass containing a reference to a superclass object is imposed at the top, in the Java API • The programmer makes use of the pattern by making a hierarchy of classes underneath that
Each concrete filter will be constructed by passing in a writer • All concrete filters ultimately descend from the Writer class • Therefore, when constructing instances of a concrete filter, any other kind of filter can be passed in • This goes back to CS 202, where you learn that you can pass in a subclass object for a superclass formal parameter
The filter subclasses may inherit or override the concrete write() methods in the OozinozFilter class • OozinozFilter also has an abstract write() method • This will have to be implemented
The filter classes are going to do their work, and differ, according to the functionality of their implementation of that method • The write() method in question takes a single int at a time as its input parameter, representing a character
The code for the OozinozFilter class is given on the next overhead • It shows the implementations of the concrete methods and the definition of the abstract method
public abstract class OozinozFilter extends FilterWriter • { • protected OozinozFilter(Writer out) • { • super(out); • } • public void write(char cbuf[], int offset, int length) throws IOException • { • for (inti = 0; i < length; i++) • write(cbuf[offset + i]); • } • public abstract void write(int c) throws IOException; • public void write(String s, int offset, int length) throws IOException • { • write(s.toCharArray(), offset, length); • } • }
In file I/O there is no effective difference between the char and int types • In the previous code the first concrete write() method depends on the abstract write() method for its implementation • This call in the concrete method: • write(cbuf[offset + i]); • Uses this method: • public abstract void write(int c) throws IOException;
You may already foresee how the subclasses will achieve their goal of different function • It is the usual trickery based on polymorphism and dynamic binding • By changing the implementation of the “used” method in the subclass, the function of the method that uses it, inherited from the superclass, will be changed
The second concrete method in the subclass takes a String as a parameter • When writing the string, it makes this call: • write(s.toCharArray(), offset, length); • In other words, it makes use of the other concrete method • Therefore, the results of this method ultimately rely on the implementation of the abstract write() method too
The overall end result is this: • You can call any of the write() methods on an instance of one of the filter classes • When you do so, whatever formatting was in the simple write() method that had to be implemented in the subclass will apply to all of the write() methods • The code for the concrete, LowerCaseFilter class is shown on the next overhead
public class LowerCaseFilter extends OozinozFilter • { • public LowerCaseFilter(Writer out) • { • super(out); • } • public void write(int c) throws IOException • { • out.write(Character.toLowerCase((char) c)); • } • }
Calling a write() method on an instance of this class changes all text to lower case • The implementation is not difficult • You don’t even have to do anything like add or subtract Unicode values • You get to rely on a method that already exists in the Character class
On the next overhead an example program is given which uses the LowerCaseFilter • This program doesn’t write to a file • Instead, it writes to the console, but this makes no difference • The code illustrates nested construction, or composition • It will output the String with the strange capitalization in all small letters
public class ShowLowerCase • { • public static void main(String[] args) throws IOException • { • Writer out = new ConsoleWriter(); • out = new LowerCaseFilter(out); • out.write("This Text, notably ALL in LoWeRcasE!"); • out.close(); • } • }
For better or worse, it has to be noted that the ConsoleWriter class used in the previous example is not a class in the Java API • It doesn’t exist yet… • At the end of this section the book gives the writing of such a class as a challenge • (You may observe that it would be sort of a relative of MyTerminalIO)
The UpperCaseFilter class works the same was as the LowerCaseFilter class • The only difference is a minor one in the implementation of the write() method, which is shown below • public void write(int c) throws IOException • { • out.write(Character.toUpperCase((char) c)); • }
Next the book gives the TitleCaseFilter class • It capitalizes every word in a string which follows white space • It would be considerably more complicated if you tried to follow the real rules for capitalizing titles • The code is given on the next overhead
public class TitleCaseFilter extends OozinozFilter • { • booleaninWhite = true; • public TitleCaseFilter(Writer out) • { • super(out); • } • public void write(int c) throws IOException • { • out.write(inWhite ? Character.toUpperCase((char) c) : Character.toLowerCase((char) c)); • inWhite = Character.isWhitespace((char) c) || c == '"'; • } • }
Next the book gives the CommaListFilter class • It puts a comma and a space after every item that’s written • It would be considerably more complicated if you tried to insert commas between any words separated by white space in the String to be written • The code is given on the next overhead
public class CommaListFilter extends OozinozFilter • { • protected booleanneedComma = false; • public CommaListFilter(Writer writer) • { • super(writer); • } • public void write(int c) throws IOException • { • if (needComma) • { • out.write(','); • out.write(' '); • } • out.write(c); • needComma = true; • } • public void write(String s) throws IOException • { • if (needComma) • out.write(", "); • out.write(s); • needComma = true; • } • }
Keep in mind that the general plan of the filter classes is the same • They take a parameter to be written and they “decorate” it or modify it in some way before writing it out • The LowerCaseFilter, UpperCaseFilter, and TitleCaseFilter classes changed the characters • The CommaListFilter added characters to the output
Challenge 27.1 • Write the code for RandomCaseFilter.java.
Solution 27.1 • One solution is: • [See the next overhead.]
public class RandomCaseFilter extends OozinozFilter • { • public RandomCaseFilter(Writer out) • { • super(out); • } • public void write(int c) throws IOException • { • out.write(Math.random() < .5 ? Character.toLowerCase((char) c) • : Character.toUpperCase((char) c)); • } • }
Next the book mentions the WrapFilter class • It takes as an input parameter both a Writer and a line length • It has the effect of eating up unnecessary white space and adding enough at the beginning of a line of text to center it • The book doesn’t bother to give the code because it is too long and complex