Recommended: Sing it, brah! 5 fabulous songs for developers
JW's Top 5
Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs
Software decorators, like cell phone faceplates, encapsulate some functionality that you can snap onto an existing object; for example, here's a code fragment that snaps sorting onto an existing Swing table model:
TableSortDecorator sortDecorator = new TableSortDecorator(table.getModel()); table.setModel(sortDecorator);
The preceding code swaps out a table's model for a decorator. After the swap, whenever the table accesses its model, it unknowingly accesses the sort decorator. The decorator adds sorting capabilities to the model it decorates, and delegates other functionality to the real model.
Like cell phones, software heavily reliant on inheritance includes a high percentage of statically defined objects. Just as a cell phone's antenna or speaker is statically defined at the factory, base classes and their extensions are statically defined at compile time. Because inheritance is static, it does not allow you to swap out an aspect of an object's behavior at runtime (such as the table model decorator in the code fragment above). Because you can use decorators much like Legos to snap together an object with desired behaviors at runtime, the Decorator design pattern proves far more flexible than inheritance.
In this article, I first introduce the Decorator pattern, starting with an example that shows how to use Java's I/O decorators. I then outline the Decorator pattern's statics and dynamics with UML class and sequence diagrams, respectively. I conclude with an example decorator that, as alluded to above, adds sorting to Swing tables.
Note: In the first installment of this column -- "Amaze Your Developer Friends with Design Patterns" -- I introduced Decorator, among other patterns. You may wish to read that introduction before proceeding with this more detailed look.
Decorator: Attaches responsibilities to objects at runtime. Decorators prove more flexible than inheritance, which attaches responsibilities to classes at compile time.
In "Amaze Your Developer Friends with Design Patterns," I described input streams and the Decorator pattern. Example 1 from that article demonstrates instantiating a line number reader:
Example 1. Instantiating I/O decorators
1. FileReader frdr = new FileReader(filename); 2. LineNumberReader lrdr = new LineNumberReader(frdr);
The preceding code creates a reader -- lrdr -- that reads from a file and tracks line numbers. Line 1 creates a file reader (frdr), and line 2 adds line-number tracking.
At runtime, decorators forward method calls to the objects they decorate. For example, in the code above, the line number
reader, lrdr, forwards method calls to the file reader, frdr. Decorators add functionality either before or after forwarding to the object they decorate; for example, our line number
reader tracks the current line number as it reads from an input stream.