Refactoring for Learning Design


We use refactoring both to improve the quality of student programs and to help students understand the basic tenets of object-oriented software design.

For many years, we used a form of apprentice learning in which we provided simple, elegant designs that students implemented in solving problems [Astrachan+1997]. The idea was to instill a sense of elegance by experiencing our designs. However, students were not able to internalize the design principles simply by filling in a finished design. Students would not use the principles in our designs because they could not appreciate them as being useful in solving problems: They appreciated the designs only as rules to follow to receive a good grade.

Now we ask students to develop the simplest (to them) working solution they can to solve a problem. We then ask them to change their solutions to accommodate changes in the problem specification. We help them understand how to refactor their solutions to incorporate design patterns and fundamental software design principles that they are able to appreciate in a more visceral way because their solutions can be refactored to accommodate the changes.

For example, we start with a series of examples from [Budd1998] that introduce, first, a simple bouncing-ball simulation, then a game that fires cannonballs at a target, and finally a pinball game. During these examples, we build the inheritance hierarchy shown in Figure 21.1 for the balls used in each game, in which each kind of ball responds differently to the move() message.

Figure 21.1. Initial ball inheritance hierarchy

graphics/21fig01.gif

The students are then asked to allow the balls to decelerate, or not, in any of the programs (according to friction or some other property). Initially, they create an additional subclass for each kind of ball, leading to the hierarchy shown in Figure 21.2.

Figure 21.2. First attempt at extending the ball inheritance hierarchy

graphics/21fig02.gif

For most students, this is a simple solution, easy to understand and implement. However, the students also realize that there is a lot of duplicated code, because each decelerating subclass changes move() in the same way. In particular, it is easy to motivate that a change made to one subclass will need to be made in all the subclasses. Moreover, any new kinds of balls will need a decelerating subclass in addition to their own.

Students understand that this is not an ideal solution and are primed to find a better way to solve this problem. Because all balls adhere to the same interface, they can be substituted for each other. A movable ball can be used where a cannonball can or where a decelerating pinball can. Using this principle, we show students how to implement a decelerating ball that takes another kind of ball as an argument and delegates the bulk of its work to that ball, and how to add its decelerating behavior. We show the students the diagram, shown in Figure 21.3 , that characterizes our solution and ask them to refactor their first solution to fit this model.

Figure 21.3. Refactored ball inheritance hierarchy

graphics/21fig03.gif

In this case, they are using the decorator pattern but do not know it as such [Gamma+1995]. After going through another example, we show them the general pattern, but by then they have internalized it and can explain when it is useful. Instead of telling them the pattern and asking them to understand it from some abstract description, we have shown a concrete example and motivated them to find a better solution (which just happens to be one for which we already have a name).



Extreme Programming Perspectives
Extreme Programming Perspectives
ISBN: 0201770059
EAN: 2147483647
Year: 2005
Pages: 445

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net