一,接口设计
使用polygon函数来画一个50边形,来接近一个圆。写circle这个函数了,需要半径r作为一个参数:
$ cat circle.py
#!/bin/bash
import math
def circle(t, r):
circumference = 2 * math.pi * r
n = 50
length = circumference / n
polygon(t, n, length)
第一行计算了圆的周长,使用2乘以圆周率再乘以半径r。这个计算用到了圆周率,所以要导入math模块。通常都要把导入语句放到整个脚本的开头。
n是我们用来逼近一个圆所用的线段数量,所以length就是每一个线段的长度了(周长circumference/线段数量n)。polygon画一 个50边的多边形,来近似做一个半径为r的圆。
这种方案的一个局限性就是n是常数,就意味着对于一些大尺寸的圆,线段数目就太多了;而对小的圆,又浪费了很多小线段。
解决的方法就是进一步扩展函数,让函数把n也作为一个参数。这就会让用户(调用circle函数的任何人)有更多决定权,可以控制所用的线段数量,当然,接口就不那么简洁了。
函数的接口:就是关于它如何工作的一个概述,比如都有什么变量? 函数实现什么功能? 以及返回值是什么? 允许调用者随意操作而不用处理一些无关紧要的细节,这种函数接口就是简洁的。
在本例中,r包含于接口内,因为要用它来确定所画圆的大小。n就不那么合适了,因为它是用来处理如何具体绘制一个圆的。
与其让接口复杂冗余,更好的思路是让n根据周长来自适应一个合适的值:
$ cat circle.py
#!/bin/bash
import math
def circle(t, r):
circumference = 2 * math.pi * r
n = int(circumference / 3) + 1
length = circumference / n
polygon(t, n, length)
现在线段数量就是周长的三分之一了,因此每段线段的长度近似为3,这个大小可以让圆看着不错,也对任意大小的圆都适用。
二,重构
当写circle这个函数的时候,我们能利用多边形函数polygon是因为一个足够多边的多边形和圆很接近。但圆弧就不太适合这个思路了,我们不能用多边形或者圆来画一个圆弧。
一个替代的方法就是把polygon修改一下,转换成圆弧:
$ cat circle.py
#!/bin/bash
import math
def circle(t, r, angle ):
arc_length = 2 * math.pi * r * range / 360
n = int(arc_length / 3) + 1
step_length = arc_length / n
step_angle = angle / n
for i in range(n):
t.fd(length)
t.lt(angle)
这个函数的后半段看着和多边形那个还挺像的,但必须修改一下接口才能重利用多边形的代码。
我们在多边形函数上增加angle(角度)作为第三个参数,但继续叫多边形就不太合适了,因为不闭合。所以就改名叫它多段线polyline:
t.lt(angle)
现在就可以用多段线polyline来重写多边形polygon和圆弧arc:
$ cat circle.py
#!/bin/bash
import math
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 * range / 360
n = int(arc_length / 3) + 1
step_length = arc_length / n
step_angle = float(angle) / n
polyline(t, n, step_length, step_angle)
最终就可以用圆弧arc来重写circle:
def circle(t, r):
arc(t, r, 360)
这个过程中,改进了接口设计,增强了代码再利用,这就叫做重构。
在本例中, 我们先是注意到圆弧arc和多边形polygon有相似的代码,所以,把他们都用多段线polyline 来实现。
如果我们事先进行了计划,估计就会先写出多段线函数polyline,然后就不用重构了,
但我们在开始一个项目之前往往不一定了解的那么清楚。一旦开始编码了,你就逐渐更理解其中的问题了。
有时候重构就意味着你已经学到了新的内容了。
结束。