This is an archived version of the course. Please find the latest version of the course on the main webpage.

Chapter 8: Composition and abstraction

Abstraction

face Josiah Wang

Compare the following 6 pieces of code. They all do the same thing. But which ones feel easier to read and less overwhelming to understand? Which ones will you find easier to understand one year after you have written it?

sin_x = math.sin(30/360.0 * (2*3.141592653589793))
sin_x = math.sin(30/360.0 * (2*math.pi))
x_in_degrees = 30
sin_x = math.sin(x_in_degrees/360.0 * (2*math.pi))
x_in_degrees = 30
x_in_degrees_normalised = x_in_degrees / 360.0
sin_x = math.sin(x_in_degrees_normalised * (2*math.pi))
x_in_degrees = 30
sin_x = math.sin(math.radians(x_in_degrees))
x_in_degrees = 30
x_in_radians = math.radians(x_in_degrees) 
sin_x = math.sin(x_in_radians)

All six pieces of code are equally valid, but they are with different degrees of abstraction. Some are longer but are easier to understand, while others are more compact but harder to decipher.

The first version is implemented in a single line. There may be times when such compact code is what you need. Nevertheless, it also requires you to use your brainpower quite a bit to decipher and understand the code. What is 30? What is 360.0? Why are you dividing 30 by 360.0? What in the world is 3.141592653589793 and why multiply it by 2?

The second version at least abstracts the value of \pi. We immediately know that the calculation uses 2\pi, something to do with a circle. Who really needs to know the exact value of \pi anyway?

The first two versions are also obviously not reusable (what if you need to compute the sine of another number?)

The third version abstracts out the 30 and gives it a name - x_in_degrees, making it easier to interpret the code. You immediately know what 30 is just by reading the code.

The fourth version abstracts the division by 360 to indicate that this is a normalisation process. We now understand what the division by 360 is for.

The fifth version completely abstracts the degree to radians conversion process with the function math.radians(). Sometimes you may not even need or want to know how to convert an angle to radians. You just need it converted!

The final version is more verbose than the fifth version. It explicitly performs the radians conversion as a separate step and assigns it to a variable, before computing the sine. Depending on the context, this might sometimes be useful for better clarity. At other times, it is a bit redundant, and it might be better to just be more concise as in the fifth version. It all boils down to what you want to achieve with your code.

To summarise, try to strike an optimal balance between readability and being compact, depending on what you are trying to achieve. You do not want to be too compact to the point that your code becomes hard to understand. You also do not want to be too verbose until your code looks way too long and too complex.

Going one step further!

You can even go to a higher level of abstraction, and create your own function that computes the sine directly from degrees (you will be writing your own functions later in Lesson 5). You can thus hide the implementation of the degree-to-radians conversion and calculation from the outer world. At this level, you are really only interested in computing the value of sine of an angle in degrees, and do not really really care how it is done!

x_in_degrees = 30
sin_x = compute_sin_from_degrees(x_in_degrees)

Another advantage - you can also reuse this function in the future, even in other programs!

We will revisit this topic of abstraction again in Lesson 5. For now, just remember - highly abstracted programs are easier to read, write and reuse!