With 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:
The 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(ArrayList): def sort(self, Trash t): /* ... */
That is, TrashSorter is an ArrayList of references to ArrayLists of Trash references, and with add( ) you can install another one, like so:
TrashSorter ts = TrashSorter() ts.add(ArrayList())
Now, 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 eachArrayList. 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:
TrashSorter needs to call each grab( ) method and get a different result depending on what type of Trash the current ArrayList is holding. That is, each ArrayList 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.
A basic OOP design principle is “Use data members for variation in state, use polymorphism for variation in behavior.” Your first thought might be that thegrab( ) method certainly behaves differently for an ArrayList 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.
The constructor for this Tbin requires that you hand it the Class of your choice. This tells the ArrayList 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.
Here is the new version of the program:
# PatternRefactoring/recycleb/RecycleB.py # Containers that grab objects of interest. # A container that admits only the right type # of Trash (established in the constructor): class Tbin: def __init__(self, binType): self.list = ArrayList() self.type = binType def grab(self, t): # Comparing class types: if(t.getClass().equals(self.type)): self.list.add(t) return True # Object grabbed return False # Object not grabbed def iterator(self): return self.list.iterator() class TbinList(ArrayList): def sort(self, Trash t): Iterator e = iterator() # Iterate over self while(e.hasNext()) if(((Tbin)e.next()).grab(t)) return # Need a Tbin for this type: add(Tbin(t.getClass())) sort(t) # Recursive call class RecycleB(UnitTest): Bin = ArrayList() TbinList trashBins = TbinList() def __init__(self): ParseTrash.fillBin("../trash/Trash.dat",bin) def test(self): Iterator it = bin.iterator() while(it.hasNext()) trashBins.sort((Trash)it.next()) Iterator e = trashBins.iterator() while(e.hasNext()): Tbin b = (Tbin)e.next() Trash.sumValue(b.iterator()) Trash.sumValue(bin.iterator()) def main(self, String args): RecycleB().test()
Tbin contains a Class reference type which establishes in the constructor what what type it should grab. The grab() method checks this type against the object you pass it. Note that in this design, grab() only accepts Trash objects so you get compile-time type checking on the base type, but you could also just accept Object and it would still work.
TTbinList holds a set of Tbin references, so that sort( ) can iterate through the Tbins when it’s looking for a match for the Trash object you’ve handed it. If it doesn’t find a match, it creates a new Tbin for the type that hasn’t been found, and makes a recursive call to itself - the next time around, the new bin will be found.
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.