SpecialistOff.NET / Вопросы / Статьи / Фрагменты кода / Резюме / Метки / Помощь / Файлы
НазадМетки: python
The nature of this problem is that the trash is thrown unclassified into a single bin, so the specific type information is lost. But later, the specific type information must be recovered to properly sort the trash. In the initial solution, RTTI (described in Thinking in Java) is used.
This is not a trivial design because it has an added constraint. That’s what makes it interesting-it’s more like the messy problems you’re likely to encounter in your work. The extra constraint is that the trash arrives at the trash recycling plant all mixed together. The program must model the sorting of that trash. This is where RTTI comes in: you have a bunch of anonymous pieces of trash, and the program figures out exactly what type they are:
# PatternRefactoring/recyclea/RecycleA.py # Recycling with RTTI. class Trash: def __init__(self, wt): self.weight = wt abstract def getValue() def getWeight(): return weight # Sums the value of Trash in a bin: def sumValue(Iterator it): val = 0.0f while(it.hasNext()): # One kind of RTTI: # A dynamically-checked cast Trash t = (Trash)it.next() # Polymorphism in action: val += t.getWeight() * t.getValue() print ( "weight of " + # Using RTTI to get type # information about the class: t.getClass().getName() + " = " + t.getWeight()) print("Total value = " + val) class Aluminum(Trash): val = 1.67f def __init__(self, wt): Trash.__init__(wt) def getValue(): return self.val setValue(newval): val = newval class Paper(Trash): val = 0.10f def __init__(self, wt): Trash.__init__(wt) def getValue(): return self.val def setValue(self, newval): val = newval class Glass(Trash): val = 0.23f def __init__(self, wt): Trash.__init__(wt) def getValue(self): return self.val def setValue(self, newval): val = newval class RecycleA(UnitTest): bin = ArrayList() glassBin = ArrayList() paperBin = ArrayList() alBin = ArrayList() def __init__(self): # Fill up the Trash bin: for(int i = 0 i < 30 i++) switch((int)(Math.random() * 3)): case 0: bin.add(new Aluminum(Math.random() * 100)) break case 1: bin.add(new Paper(Math.random() * 100)) break case 2: bin.add(new Glass(Math.random() * 100)) def test(self): Iterator sorter = bin.iterator() # Sort the Trash: while(sorter.hasNext()): Object t = sorter.next() # RTTI to show class membership: if(t instanceof Aluminum) alBin.add(t) if(t instanceof Paper) paperBin.add(t) if(t instanceof Glass) glassBin.add(t) Trash.sumValue(alBin.iterator()) Trash.sumValue(paperBin.iterator()) Trash.sumValue(glassBin.iterator()) Trash.sumValue(bin.iterator()) def main(self, String args[]): RecycleA().test()
In the source code listings available for this book, this file will be placed in the subdirectory recyclea that branches off from the subdirectorypatternRefactoring. The unpacking tool takes care of placing it into the correct subdirectory. The reason for doing this is that this chapter rewrites this particular example a number of times and by putting each version in its own directory (using the default package in each directory so that invoking the program is easy), the class names will not clash.
Several ArrayList objects are created to hold Trash references. Of course, ArrayLists actually hold Objects so they’ll hold anything at all. The reason they hold Trash (or something derived from Trash) is only because you’ve been careful to not put in anything except Trash. If you do put something “wrong” into the ArrayList, you won’t get any compile-time warnings or errors-you’ll find out only via an exception at run time.
When the Trash references are added, they lose their specific identities and become simply Object references (they are upcast). However, because of polymorphism the proper behavior still occurs when the dynamically-bound methods are called through the Iterator sorter, once the resulting Object has been cast back to Trash. sumValue( ) also takes an Iterator to perform operations on every object in the ArrayList.
It looks silly to upcast the types of Trash into a container holding base type references, and then turn around and downcast. Why not just put the trash into the appropriate receptacle in the first place? (Indeed, this is the whole enigma of recycling). In this program it would be easy to repair, but sometimes a system’s structure and flexibility can benefit greatly from downcasting.
The program satisfies the design requirements: it works. This might be fine as long as it’s a one-shot solution. However, a useful program tends to evolve over time, so you must ask, “What if the situation changes?” For example, cardboard is now a valuable recyclable commodity, so how will that be integrated into the system (especially if the program is large and complicated). Since the above type-check coding in the switch statement could be scattered throughout the program, you must go find all that code every time a new type is added, and if you miss one the compiler won’t give you any help by pointing out an error.
The key to the misuse of RTTI here is that every type is tested. If you’re looking for only a subset of types because that subset needs special treatment, that’s probably fine. But if you’re hunting for every type inside a switch statement, then you’re probably missing an important point, and definitely making your code less maintainable. In the next section we’ll look at how this program evolved over several stages to become much more flexible. This should prove a valuable example in program design.