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
Page 6 of 6
Figure 3H shows the class diagram for the filter, which decorates a table model:
Figure 3H. Filter decorator class diagram. Click on thumbnail to view full-size image.
TableHighPriceFilter, by way of TableModelDecorator, implements the TableModel interface and forwards method calls to an enclosed table model instance -- the realModel. TableHighPriceFilter implements TableFilterDecorator, which is listed in Example 3H.
Note: The TableModelDecorator class is listed in "Decorate Your Java Code."
import javax.swing.table.TableModel;
public abstract class TableFilterDecorator extends
TableModelDecorator {
// Extensions of TableFilterDecorator must implement the
// abstract filter method, in addition to tableChanged. The
// latter is required because TableModelDecorator
// implements the TableModelListener interface.
abstract public void filter(int column);
public TableFilterDecorator(TableModel realModel) {
super(realModel);
}
}
Example 4H lists the TableHighPriceFilter class:
import javax.swing.table.TableModel;
import javax.swing.event.TableModelEvent;
public class TableHighPriceFilter extends TableFilterDecorator {
// The lone constructor must be passed a reference to a
// TableModel. This class decorates that model with additional
// sorting functionality.
public TableHighPriceFilter(TableModel model) {
super(model);
allocate();
}
// tableChanged is defined in TableModelListener, which
// is implemented by TableFilterDecorator.
public void tableChanged(TableModelEvent e) {
allocate();
}
// Three TableModel methods are overridden from
// TableModelDecorator.
public Object getValueAt(int row, int column) {
return getRealModel().getValueAt(indexes[row], column);
}
public void setValueAt(Object aValue, int row, int column) {
getRealModel().setValueAt(aValue, row, column);
}
public int getRowCount() {
return rowCount == NOT_INITIALIZED ?
getRealModel().getRowCount() : rowCount;
}
// The filter method filters out items that cost more than
// one dollar.
public void filter(int column) {
int rowsNow = getRowCount();
int[] newIndexes = new int[rowsNow];
int newRowCount = rowsNow;
for(int i=0, j=0; i < rowsNow && j < rowsNow;) {
String price = (String)getRealModel().
getValueAt(indexes[j], column);
if(price.startsWith("$.")) { // less than a dollar
newIndexes[i++] = indexes[j++];
}
else {
newRowCount--;
++j;
continue;
}
}
rowCount = newRowCount;
for(int i=0; i < rowCount; ++i)
indexes[i] = newIndexes[i];
}
private void allocate() {
indexes = new int[getRowCount()];
for(int i=0; i < indexes.length; ++i) {
indexes[i] = i;
}
}
private int indexes[];
private static int NOT_INITIALIZED = -1;
private int rowCount = NOT_INITIALIZED;
}
The TableHighPriceFilter class inherits forwarding methods -- which forward to the actual model -- from TableModelDecorator. For some decorators, forwarding methods can constitute a good deal of code, so it's convenient to encapsulate those methods
in a base class for other decorators to extend.
Thanks for all the email you've sent in response to the first two Java Design Patterns installments. I've had some interesting discussions about design patterns and object-oriented (OO) software development. Here's one such exchange that points out the differences between object composition and delegation, and why the Decorator pattern uses the former but not the latter:
Tom Palmer wrote:
Decorators are a form of dynamic inheritance system, but with normal OO inheritance, no matter where a method is implemented in a hierarchy,thisalways refers to the same object. With normal decorators, however, once a call is passed on, the wrapped object doesn't know about the original object. It's as if the super method call forgot whothiswas.
There's an almost convenient mechanism for passing the
thisreference: two versions of each method, one with an extra parameter representing the effectivethis, which I call the target. The short form of each method just calls the expanded form with the currentthisas the target. Automatic design patterns via proxies can be made to look for expanded methods and pass in the proper target object.
Palmer makes a good point. Instead of comparing decorators to inheritance, it's more appropriate to compare the more general composition or delegation techniques (which he describes above) to inheritance.
Decorators use object composition by forwarding method calls to an enclosed object. It's called composition because the enclosed
object cannot access its enclosing object (the this reference he discusses above).
Delegation is just like composition, except the enclosing object passes a reference to itself to the enclosed instance. There are several ways for the enclosing object to make itself known to the enclosed object -- what Palmer describes above is one way.
For the Decorator pattern, you use composition instead of delegation, because delegation requires the enclosed object to be aware of its enclosing object. One of the Decorator pattern's main attractions is that enclosed objects (and the objects that use the decorators) are oblivious to decorators. For example, in one example discussed in "Decorate Your Java Code," table models remain unaware of the sorters that decorate them, so that any table model can be sorted.
Read more about Core Java in JavaWorld's Core Java section.