(Coursenotes for CSC 203 Project-based Object-oriented Programming and Design)

Procedural and Object-oriented Programming

Overview

In this lesson, we’ll compare and contrast two styles of programming, or programming paradigms: procedural programming and object-oriented programming (the latter being the focus of this course).

Objectives

After this lesson, you will know about:

Procedural and object-oriented programming

Procedural programming is a style of programming that focuses on providing a series of instructions to be executed. These would the statements in the program. A procedural program can also bundle up instructions into functions that can be used and re-used as needed. Similarly, data can be bundled up into compound objects, simply for the purposes of packaging up related pieces of information.

Procedural programming was the dominant way of doing things until Object-oriented programming (OOP) was invented in the 70s and 80s. As programming projects started to get larger and more complex, because software was quickly being integrated into many domains, software engineers started to run into problems with software maintainability. In procedural programming, it is easy to get into a “spaghetti code” situation, where you have a large program with tons of functions operating on a number of data structures. The onus is on the programmer to structure the program so that it would be easily readable, testable, and changeable.

One proposed solution was Object-oriented programming (OOP).

In OOP, instead of focusing our program on data and functions that operate on that data, we use objects, which bundle up related data and behaviours that operate on those data into classes.

Orienting our programs around objects (the argument goes) lets us modularise the various parts of a large software system, with each part only reading or affecting its own data. This leads to a number of beneficial effects:

Of course, all of the above are hypothetical. It’s perfectly possible to gain all of the above benefits using procedural programming, just like it is possible to write terrible, unmaintainable programs using OOP.

In this class, we’re going to aim to understand the higher-level principles of good software design, using OOP as a vehicle to put those principles into practice. This does not mean that those principles are unique to OOP.

This is a good time to note a few things!

Ok, let’s look at examples! We’ll look at the same super-simple program written in each of the two styles.

Procedural programming

Consider the code below.

We have the Pitcher class, which is simply a bundle of data members. You can think of this as more-or-less like a dict in Python, or a struct in C.

The Pitcher class above only has data, and has no behaviours. Unlike the CsCohort that we looked at in the previous lesson, the Pitcher cannot perform any actions itself, it cannot do anything using its data. Any behaviours we want to perform must be written as separate functions — those functions will take the Pitcher object as a parameter, and perform actions using the Pitcher’s data.

Any behaviours that use the Pitcher’s data must be written in separate functions, which will take the Pitcher object as a parameter. Those are written in the PitcherUtil class.

Object-oriented programming

Here is the same program written in an Object-oriented style. Please take some time to read through the annotations within the source code.

“static” data and methods

The code above also introduces the notion of static data. Here, the word “static” is used slightly differently than in the phrase “statically-typed”. In this context, static means the value belongs to the class, as opposed to individual instance of the class.

So in the example below, since all baseball games are 9 innings long, the INNINGS_PER_GAME variable is marked static, and the value is shared by all Pitcher objects.

Why don’t we just give each Pitcher its own inningsPerGame instance variable and have its value be 9 for all Pitchers?

Just like we can have both static and instance variables, we can also have both static and instance methods. A static method would define behaviours that don’t belong to or apply to any individual Pitcher object.

For example, you might have a static method that takes in a list of Pitcher objects (List<Pitcher>) and computes the average number of runs scored across all those Pitchers.

That is, we could have the following method inside the Pitcher class:

public static double averageRuns(List<Pitcher> pitchers) {
  double sum = 0;
  for (Pitcher p : pitchers) {
    sum = sum + p.runs();
  }

  if (pitchers.size() != 0) {
    return sum / pitchers.size();
  } else {
    return 0.0;
  }
}

What would this refer to in the method above?1

Main takeaways regarding static methods.

  1. There is no this inside a static method, since the method is not being called by any object. It’s being called by the class itself. Any reference to this inside a static method would cause your code to not compile.