Java Reference
In-Depth Information
}
public void print(Document d, int nrCopies, boolean printDuplicate) {
printer.print(d, nrCopies, printDuplicate);
}
}
Note how this class reimplements all the methods from the original
Printer
interface and passes on
every command to the contained
Printer
instance (again, this is an example of the adapter pattern).
Instead of using the
Printer
interface in your client code, you now use
AbstractPrintSpooler
,
however. Note that you can also decide to have
AbstractPrintSpooler
implement the
Printer
interface (this forces the abstraction to also implement all methods from the implementation) and to
make the class nonabstract if you want to use it as is (which is also perfectly valid). All things con-
sidered, it does seem that the
AbstractPrintSpooler
class is adding nothing of value, so then why
would you add another layer of abstraction? The reason is that you can now add concrete subclasses
of this abstraction. Imagine for example a
GreenPrintSpooler
like so:
public class GreenPrintSpooler extends AbstractPrintSpooler {
public GreenPrintSpooler(Printer printer) {
super(printer);
}
public void print(Document d, int nrCopies, boolean printDuplicate) {
printDuplicate = true; // Force to print duplicates
d.setToBlackAndWhite(); // Convert document to black and white
print(d, nrCopies, printDuplicate);
}
}
A concrete implementation of your spooler has now been created that will convert all documents to
black and white and force the duplicate option to true before passing on the
print
command to the
implementation. The benefits of this pattern should become clear to you now: bridges are helpful
whenever an extra layer of abstraction is needed on top of something else. For example, whether to
print green is a separate decision on top of the printer model being used.
Decorator Pattern
The decorator pattern also bears similarities to the adapter pattern. It allows you to add behavior
to a single instance of a class without adding this behavior to all instances of the same class (as you
would end up with through normal subclassing).
This pattern is commonly used in GUI programming, so it makes sense to include an example from
this context. Imagine you have created a
Window
class to display fancy GUI windows. Now let's say
you want to create a particular variant of these windows that cannot be minimized and needs to
stay on-screen. You can simply create a
NaggingWindow
class that extends the
Window
class and adds
behavior to disable the minimize button. Imagine that, sometime later, you also encounter a need to
add scrollbars to some of your windows. No problem, as you can just create a
ScrollableWindow
class to do this. However, what about windows that should not be minimized and also need
scrollbars? One solution might be to add a
NaggingScrollableWindow
class, but as windows get
more and more features, this would quickly lead to a soup of redundant subclasses. One solution is
to merge everything in the single
Window
class, but this would lead to a single, horribly confusing
Search WWH ::
Custom Search