Bruce Eckel’s Thinking in Java | Contents | Prev | Next |
creation out of the way, it’s time to tackle the remainder of the design:
where the classes are used. Since it’s the act of sorting into bins
that’s particularly ugly and exposed, why not take that process and hide
it inside a class? This is the principle of “If you must do something
ugly, at least localize the ugliness inside a class.” It looks like this:
TrashSorter
object initialization must now be changed whenever a new type of
Trash
is added to the model. You could imagine that the
TrashSorter
class might look something like this:
class TrashSorter extends Vector { void sort(Trash t) { /* ... */ } }
is,
TrashSorter
is a
Vector
of handles to
Vectors
of
Trash
handles, and with
addElement( )
you can install another one, like so:
ts = new TrashSorter();
ts.addElement(new
Vector());
however,
sort( )
becomes a problem. How does the statically-coded method deal with the fact that
a new type has been added? To solve this, the type information must be removed
from
sort( )
so that all it needs to do is call a generic method that takes care of the
details of type. This, of course, is another way to describe a
dynamically-bound method. So
sort( )
will simply move through the sequence and call a dynamically-bound method for
each
Vector.
Since the job of this method is to grab the pieces of trash it is interested
in, it’s called
grab(Trash).
The structure now looks like:
needs to call each
grab( )
method and get a different result depending on what type of
Trash
the current
Vector
is holding. That is, each
Vector
must be aware of the type it holds. The classic approach to this problem is to
create a base “
Trash
bin” class and inherit a new derived class for each different type you
want to hold. If Java had a parameterized type mechanism that would probably be
the most straightforward approach. But rather than hand-coding all the classes
that such a mechanism should be building for us, further observation can
produce a better approach.
basic OOP design principle is “Use data members for variation in state,
use polymorphism
for variation in behavior.” Your first thought might be that the
grab( )
method certainly behaves differently for a
Vector
that holds
Paper
than for one that holds
Glass.
But what it does is strictly dependent on the type, and nothing else. This
could be interpreted as a different state, and since Java has a class to
represent type (
Class)
this can be used to determine the type of
Trash
a particular
Tbin
will hold.
constructor for this
Tbin
requires
that you hand it the
Class
of your choice. This tells the
Vector
what type it is supposed to hold. Then the
grab( )
method uses
Class
BinType
and RTTI to see if the
Trash
object you’ve handed it matches the type it’s supposed to grab.
is the whole program. The commented numbers (e.g. (*1*) ) mark sections that
will be described following the code.
//: RecycleB.java // Adding more objects to the recycling problem package c16.recycleb; import c16.trash.*; import java.util.*; // A vector that admits only the right type: class Tbin extends Vector { Class binType; Tbin(Class binType) { this.binType = binType; } boolean grab(Trash t) { // Comparing class types: if(t.getClass().equals(binType)) { addElement(t); return true; // Object grabbed } return false; // Object not grabbed } } class TbinList extends Vector { //(*1*) boolean sort(Trash t) { Enumeration e = elements(); while(e.hasMoreElements()) { Tbin bin = (Tbin)e.nextElement(); if(bin.grab(t)) return true; } return false; // bin not found for t } void sortBin(Tbin bin) { // (*2*) Enumeration e = bin.elements(); while(e.hasMoreElements()) if(!sort((Trash)e.nextElement())) System.out.println("Bin not found"); } } public class RecycleB { static Tbin bin = new Tbin(Trash.class); public static void main(String[] args) { // Fill up the Trash bin: ParseTrash.fillBin("Trash.dat", bin); TbinList trashBins = new TbinList(); trashBins.addElement( new Tbin(Aluminum.class)); trashBins.addElement( new Tbin(Paper.class)); trashBins.addElement( new Tbin(Glass.class)); // add one line here: (*3*) trashBins.addElement( new Tbin(Cardboard.class)); trashBins.sortBin(bin); // (*4*) Enumeration e = trashBins.elements(); while(e.hasMoreElements()) { Tbin b = (Tbin)e.nextElement(); Trash.sumValue(b); } Trash.sumValue(bin); } } ///:~
- TbinList
holds a set of
Tbin
handles, so that
sort( )
can iterate through the
Tbins
when it’s looking for a match for the
Trash
object you’ve handed it. - sortBin( )
allows you to pass an entire
Tbin
in, and it moves through the
Tbin,
picks out each piece of
Trash,
and sorts it into the appropriate specific
Tbin.
Notice the genericity of this code: it doesn’t change at all if new types
are added. If the bulk of your code doesn’t need changing when a new type
is added (or some other change occurs) then you have an easily-extensible system. - Now
you can see how easy it is to add a new type. Few lines must be changed to
support the addition. If it’s really important, you can squeeze out even
more by further manipulating the design. - One
method call causes the contents of
bin
to be sorted into the respective specifically-typed bins.