17.7: Refactoring
- Page ID
- 16930
When I wrote circle
, I was able to re-use polygon
because a many-sided polygon is a good approximation of a circle. But arc
is not as cooperative; we can’t use polygon
or circle
to draw an arc.
One alternative is to start with a copy of polygon
and transform it into arc
. The result might look like this:
def arc(t, r, angle): arc_length = 2 * math.pi * r * angle / 360 n = int(arc_length / 3) + 1 step_length = arc_length / n step_angle = float(angle) / n for i in range(n): fd(t, step_length) lt(t, step_angle)
The second half of this function looks like polygon
, but we can’t re-use polygon
without changing the interface. We could generalize polygon
to take an angle as a third argument, but then polygon
would no longer be an appropriate name! Instead, let’s call the more general function polyline
:
def polyline(t, n, length, angle): for i in range(n): fd(t, length) lt(t, angle)
Now we can rewrite polygon
and arc
to use polyline
:
def polygon(t, n, length): angle = 360.0 / n polyline(t, n, length, angle) def arc(t, r, angle): arc_length = 2 * math.pi * r * angle / 360 n = int(arc_length / 3) + 1 step_length = arc_length / n step_angle = float(angle) / n polyline(t, n, step_length, step_angle)
Finally, we can rewrite circle
to use arc
:
def circle(t, r): arc(t, r, 360)
This process—rearranging a program to improve function interfaces and facilitate code re-use—is called refactoring. In this case, we noticed that there was similar code in arc
and polygon
, so we “factored it out” into polyline
.
If we had planned ahead, we might have written polyline
first and avoided refactoring, but often you don’t know enough at the beginning of a project to design all the interfaces. Once you start coding, you understand the problem better. Sometimes refactoring is a sign that you have learned something.