Lecture Notes
Introduction to Programming and Algorithm Design
(COP1000)
Case study: interface design
This chapter discusses one important aspect of software
engineering: how to design functions that are useful, easytouse,
reusable, and extensible. The design feature that makes this
possible is the function's interface – what arguments it
accepts, and what value it returns. This chapter also introduces
TurtleWorld() – a graphical module
written by the author of our text to help illustrate the concepts of
encapsulation and generalization that are fundamental
to interface design.
TurtleWorld
 The TurtleWorld module is
available for download as part of a suite of modules called
Swampy, written by Allen Downey
 TurtleWorld is similar to Logo,
whose "... bestknown feature is the turtle, which is an
onscreen cursor, which can be given movement and drawing
instructions, and is used to programmatically produce line
graphics." (from it's
Wikipedia entry)
 Here is how to download and install it:
 Download Swampy by rightclicking here:
http://allendowney.com/swampy.1.1.zip
 Rightclick on the downloaded file and chose "Extract
all ..." (if you have Windows XP or above; if not, you will
need an unzip program to extract the compressed files)
 This process should produce a folder named
swampy.1.1, in which the various
modules and sample programs should have been extracted –
move this folder to somewhere convenient; you can change the
name to something simpler if you want, perhaps ending up
with the swampy modules in
c:\cop1000\swampy, for example
 To test your installation, start the Python IDLE and type
the following program in the edit window (or download the
program here), and save it to the
same folder as you saved the Swampy files:
# polygon.py  090115 (awd
p29)
# Draw polygon with Swampy
from TurtleWorld import *
def main():
world = TurtleWorld()
bob = Turtle()
fd(bob, 100)
rt(bob)
fd(bob, 100)
wait_for_user()
main() 
 Run the program, and you should see a
TurtleWorld window open, and the Turtle (bob)
trace some lines, using a few of the basic functions a Turtle
can perform:
Command 
Description 
bk(t, dist=1) 
Move Turtle t
back specified distance (default distance is 1
pixel) 
fd(t, dist=1) 
Move Turtle t
forward specified distance (default distance is 1
pixel) 
lt(t,
angle=90) 
Turn Turtle t
left by given angle (default angle is 90
degrees) 
pd(t) 
Put Turtle t's
pen down 
pu(t) 
Put Turtle t's
pen up 
rt(t, angle=90) 
Turn Turtle t
right by given angle (default angle is 90
degrees) 
 You can see all the commands available by importing
TurtleWorld in the shell, and typing
help(Turtle)
 Now modify the polygon.py
program by adding the code necessary to draw a complete square,
and save it as polygon2.py
Simple repetition
 Your code for polygon2.py
probably included four repetitions of these statements:
fd(bob, 100)
rt(bob)
(Note that for some reason the text decided to draw a
square with lt insead of
rt commands?)
 All programming languages have a way to specify that a
statement (or group of statements) is to be repeated a specified
number of times; this is called definite repetition
 In Python, one way to implement definite repetition is to
use the for statement and the builtin range function, like
this:
for v in range(n):
statement
...
In the above,
v is any variable name, and
n is the number of times you
want the statement(s)
repeated
 Rewrite polygon2.py using
definite repetition, and save it as
polygon3.py; it should look like:
# polygon3.py  090115 (awd
p31)
# Draw polygon with Swampy
from TurtleWorld import *
def main():
world = TurtleWorld()
bob = Turtle()
for i in range(4):
fd(bob, 100)
rt(bob)
wait_for_user()
main() 
Encapsulation
 We have condensed our code by using definite repetition, but
have not made it very easily reusable
 If you look at the part of our code that actually draws the
square, it will work only if the Turtle's name is
bob; we would have to change the
name in the code to make any other Turtle draw a square, like
sally in the sample program
polygon4.py
 A good solution here is to encapsulate the process of
drawing a square by putting the logic in a function; we can tell
the function which Turtle to use to draw the square by passing
it's name as an argument that will be received into a parameter
 Here is the definition of one such function we could write:
def square(t):
for i in range(4):
fd(t, 100)
rt(t) 
 When this function is called, it draws a square using Turtle
t, which is whatever Turtle you pass
it as an argument; see sample program
polygon5.py
 When we have encapsulated a function like this, it is easily
reusable because we don't have to change anything in it to make
use of it again
Generalization
 Although we have designed a reusable function, it is still
not as useful as it could be, because it only draws squares with
100 pixel sides
 One characteristic of good software engineering is functions
that are not only reusable, but that have been made flexible by
generalizing them to work in a variety of ways
 We can improve on our square function by parameterizing
the length of the side in the interface to the function:
def square(t, length):
for i in range(4):
fd(t, length)
rt(t) 
 Now a client of the function can specify by arguments
both which Turtle should draw a square, and how big the square
should be; see
polygon6.py
 There is one value in the function that is still a constant
– the number of sides drawn (4, since we are drawing a square);
what happens if we parameterize the number of sides?
def polygon(t, n, length):
angle = 360.0 / n
# turn angle
for i in range(n):
fd(t, length)
rt(t, angle) 
 Now we have a reusable, useful function that can draw any
polygon, not just a square; see
polygon7.py
Interface design
 We have a function that can draw any size polygon of any
number of sides, how about a circle? (A circle is a polygon with
an infinite number of sides)
 We could require the client to specify the number of sides
with which to approximate the circle with a polygon, and
approximate how long each side must be, but this is seldom how a
client would want to invoke a function to draw a circle; the
natural way would be to specify the radius of a circle
 We can design a function circle
that requires just two arguments, then calculates the ones
needed to approximate a circle with the
polygon function and then call it:
def circle(t, r):
circumference = 2 * math.pi * r
n = int(circumference / 3) + 1
length = circumference / n
polygon(t, n, length) 
 The number of sides needed (n)
is proportional to the circumference – the larger the
circumference, the more sides we will use to approximate it: for
a 100 pixel circumference, construct a 34sided polygon, for a
200 pixel circumference, a 67sided polygon; see
circle.py
 We have designed a function with an interface that is
naturally usable by a client, rather than forcing the client to
provide (unnatural) arguments required by a less usable function
Refactoring
 Now suppose we want to write a function
arc that draws just a portion of the circumference of a
circle, rather than a complete circle
 The circle function won't do –
it will only draw a whole circle, and there is no clear way to
modify circle to draw an arc
 But it is clear that if we had an arc function we could use
it to draw a circle; how?
 Try a new function polyline that
parameterizes the number of line segments to draw, their length,
and the angle at each turn:
def polyline(t, n, length,
angle):
for i in range(n):
fd(t, length)
rt(t, angle) 
 Now we can rewrite polygon to
call polyline:
def polygon(t, n, length):
angle = 360.0 / n
polyline(t, n, length, angle) 
 And an arc is just part of a circle (polygon) of a specified
arcangle
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) 
 And, of course, a circle is just an arc with a given radius
and an arc_angle of 360 degrees; see
circle2.py
 We finally have a set of useful, reusable functions, which a
client can use to construct arcs, circles, and polygons with a
natural interface
 We achieved this by rearranging the program to design more
elementary functions; this process is called refactoring
 It was not clear at the beginning what the best design of
our functions would be, but we learned as we went along
A development plan
 A development plan is a process for writing a program; this
case study looked at the "encapsulation and generalization"
method, that includes these steps:
 Start by writing a small program with no function
definitions
 Once you have the program working, encapsulate the
program in a function
 Generalize the function by adding appropriate parameters
 Repeat steps 13 until you have a working set of
functions
 Look for opportunities to improve the program by
refactoring, which will usually mean writing a function that
does less than the ones you have
 My maxim: By coding a problem you learn more about it. Your
second attempt is always better than you first.
docstring
 Even if you have spent some time making functions reusable
and useful, how does a client know how to use your functions?
 One way is to write a separate API (Application Programmers
Interface) that documents each function and its interface
 Another way, made simple in Python, is to include the
interface documentation in the code itself, by including a
docstring (documentation string) at the beginning of each
function; see
circle3.py
 The docstring explains the interface interface
