Object-Oriented Programming (OOP) is a programming approach based on objects and classes. The object-oriented paradigm allows us to organise software as a collection of objects that consist of both data/attributes and behaviours.
This programming concept appeared in the 1980s and most modern high level programming languages have object-oriented features. Python, Java, C++, Ruby, PHP, Javascript are all OOP programming languages. Sometimes OOP features have been retro-fitted to an existing procedural language: This is the case for C++ which is a fully OOP language based on the procedural language C. Nowadays most advanced pieces of software or video games are built using object-oriented programming concepts.
Object-Oriented Programming makes it easier to design and structure code because:
- It is based on an approach where objects represent real life objects or concepts. Similarly to real life, an OOP computer model would be structured around different objects that have their own properties and behaviours and that interact with each other. For instance in a car racing game, all the cars, the buildings even the road signs would be objects with their own attributes (Physical dimensions, positions, colours, speed, etc…) and behaviours (e.g. cars can accelerate or slow down, turn, crash, etc.)
- It facilitates the decomposition of a large system into smaller modules and help structure your code more effectively.
- It facilitates the re-usability of code.
Key Concepts
OOP Programming is based around the following key concepts:
- Classes & Objects
- Inheritance
- Encapsulation
- Polymorphism
- Abstract Classes
Classes & Objects
A class is a template or blueprint from which objects will be instantiated. It’s like the mould from which objects can be created/instantiated from. A class represents a real life concept such as a person, an animal, a car, a house or a football.
A class consists of a collection of states (a.k.a. attributes or properties) and behaviours (a.k.a. methods).
An object is an instance of a class. The attributes of an object are stored using variables for storing data and the behaviour/methods of an object are implemented as functions or procedures and can be used to perform operations on the data.
Constructor
Most classes have one method called a constructor. It is a function that is automatically called when an object is created/instantiated from a class. It is mainly used to initialise the default values for each attribute/variable of the object.
Superhero Class
Let’s consider a class class called Superhero.
The attributes of a superhero are:
- name (String)
- superpower (String)
- strength (Integer)
Some of the methods of a superhero are:
- init() – This is the constructor which may take some parameters used to initialise the default values of the superhero to be created.
- walk()
- run()
- jump()
The diagram on the right represents the Superhero Class with its attributes and its methods. Note that we do not need to represent the constructor on this diagram as it is assumed that all classes have a constructor. (In Python the constructor of a class is the __init__() function.)
Instantiating Objects from the Superhero Class
Our video game will have 3 superheroes called Spiderman, Superman and Batman. We will hence instantiate 3 objects from our Superhero Class.
spiderman = new Superhero("Spiderman","Wrist web-shooters to shoot spider web material",10) batman = new Superhero("Batman","Night vision",10) superman = new Superhero("Superman","Can fly",20)
Naming Convention
When coding, the identifier of a class starts with an uppercase letter, e.g. Superhero.
The identifier of an object starts with a lowercase letter, e.g. spiderman.
Inheritance
It is possible to organise classes into master classes and sub classes by creating a relationship between classes called inheritance. This provides a way of categorising classes and provides an efficient way to organise the code of a complex program more effectively.
For instance, let’s consider an online shop that sells different types of products (items) such as DVDs, Books, and Mp3s.
The code of such an online shop could be based around the use of a class called Item. This class would contain all the attributes and methods that each item will need:- Attributes: name, description, price
- Methods: viewFullDescription(), addToShoppingBasket(), removeFromShoppingBasket()
Sub-classes can then be defined with additional attributes and methods which are more specific. For instance:
-
MP3 Class:
- Attributes: artist, duration
- Methods: play(), download()
- Attributes: certificate, duration, actors
- Methods: viewTrailer()
- Attributes: author, genre, numberOfPages
- Methods: previewContent()
DVD Class:
Book Class:
The MP3, DVD and Book sub-classes will inherit the properties and methods of the parent class (Item) as represented in green below.
-
MP3 Class:
- Attributes: name, description, price, artist, duration
- Methods: viewFullDescription(), addToShoppingBasket(), removeFromShoppingBasket(), play(), download()
- Attributes: name, description, price, certificate, duration, actors
- Methods: viewFullDescription(), addToShoppingBasket(), removeFromShoppingBasket(), viewTrailer()
- Attributes: name, description, priceg, author, genre, numberOfPages
- Methods: viewFullDescription(), addToShoppingBasket(), removeFromShoppingBasket(), previewContent()
DVD Class:
Book Class:
Class Diagram
The inherited properties and methods of a sub class do not need to be duplicated in a class diagram, as it is understood that they are inherited from the parent class:
Terminology
The parent class is also called master class or base class.
The child class is also called sub-class or derived class.
Multiple Inheritance
It is possible for a class to inherit from two or more parent classes. In this case it will inherit the properties and methods of all the parent classes.
For example we could have a class representing hybrid cars, that would inherit the properties and method of both electric cars and petrol cars.
Encapsulation
Private vs. Public
When defining attributes and methods of a class, these can be defined as being either private or public.
A private attribute or a private method can only be accessed from methods of the class. They cannot be directly accessed from outside the class.
A public attribute or a public method can be accessed from anywhere (other methods of the class or from outside the class).
For instance, let’s consider a Car class with the following attributes and methods:
In our car racing game, we would instantiate an object called playerCar from this Car class.
playerCar = new Car("Mini","Cooper")
If all methods and properties are declared as public, then we would be able to access them from anywhere in the code. e.g.
playerCar.speed = 70 playerCar.fuelLevel = 45 playerCar.accelerate(5) # increase speed by 5mph playerCar.drive(10) # drive 10 miles PRINT("Your current fuel level is:" PRINT playerCar.fuelLevel
However, it is considered good practice to “hide” some properties or methods from the “outside world” by making these private. In this case these private properties and methods cannot be accessed from outside the class.
For instance, in the example above, we may decide that both attributes speed and fuelLevel should be set at private. In this case none of the following three lines would compile and hence these would have to be removed:
playerCar.speed = 70 playerCar.fuelLevel = 45 PRINT playerCar.fuelLevel
We could still alter the content of the fuelLevel and the speed of the car in the code of the fillUp(), accelerate(), slowDown() and drive() methods of the car.
playerCar.fillUp(45) # Add 45 litres of petrol in the tank playerCar.accelerate(50) # increase speed by 50mph playerCar.drive(10) # drive 10 miles
Getters and Setters
Very often, properties of a class are defined as being private properties, hence cannot be accessed directly from outside the class. Additional public methods called getters and setters are defined to access/read (getters) the content of a private property and to overwrite (setters) the content of a private property.
For instance, to retrieve the fuelLevel of the player car we could have a new public method called getFuelLevel() and use it to inform the user of how much petrol they have left in their tank.
PRINT "Your current fuel level is:" PRINT player.getFuelLevel()
We may also define a setter for this property in case we want to give an option for the player to reset their tank to a specific value or to empty it. e.g.
player.setFuelLevel(0) # Emptying the fuel tank
Note that using setters is also useful to prevent direct access to a property and implement validation checks within the setter methods.
For instance if the fuelLevel was a public property then the following code would be accepted even though a car cannot have 999 litres of petrol (999 > tankCapacity)!
playerCar.fuelLevel = 999
By making the fuelLevel property private and forcing the use of a setFuelLevel() public method (setter), we can implement in the code of the setFuelLevel() method a validatiom check that cannot be bypassed to ensure that the parameter given is a positive number, lower or equal to the tankCapacity. e.g.
FUNCTION setFuelLevel(quantity) IF quantity<0 THEN fuelLevel = 0 ELSE IF quantity > tankCapacity THEN fuelLevel = tankCapacity ELSE fuelLevel = quantity END IF END FUNCTION
The setter could then be used in our code as follows:
playerCar.setFuelLevel(45) # reset fuel level to 45 litres
Polymorphism
Polymorphism means “multiple forms”. In OOP these multiple forms refer to multiple forms of the same method, where the exact same method name can be used in different classes, or the same method name can be used in the same class with slightly different paramaters. There are two forms of polymorphism, over-riding and over-loading.
Over-Riding Polymorphism
Let’s consider the following class diagram, where both the ElectricCar class and the PetrolCar class inherits from the Car class.
In this class, both the PetrolCar and the ElectricCar classes inherit the drive() method from the parent Car class. However this method will have to be implemented differently as you do not dive an electric car the same way you drive a petrol car. The code used in both methods will be different and overwrite the code of the parent class. This is what over-ride polymorphism means: when a method from a subclass has to be overwritten to override the method inherited form the parent class.
On occasions, as this is the case in our example, the drive() method of the Car class is only defined but not implemented at all (an empty function, without code) in the Car class. It will be overwritten and properly implemented in the sub classes instead.
Over-Loading Polymorphism
Overloading Polymorphism is used when a method can be implemented more than once to accept different parameters.
For instance, let’s consider our Superhero class to which we would like to add a method to set the Date of Birth (DoB) of a superhero. We could write this same method several times to accept different parameters such as:
The method could accept a date parameter as a string in the “DD/MM/YYYY” format.
PROCEDURE setDoB(string:date) # date in DD/MM/YYYY format
e.g.
spiderman.setDob("15/08/1962")
The same method can be implemented a second time, taking three parameters as follows:
PROCEDURE setDoB(integer:day, integer:month, integer:year)
e.g.
spiderman.setDob(15,08,1962)
Having multiple implementations of the same method to accept different parameters (different number of parameters or parameter of different data types) is called over-loading polymorphism.
Abstract Classes
An abstract class is a class that will never be used to instantiate objects directly. The intention is to create sub-classes that will inherit from this abstract class and from which we will be able to instantiate objects from.
For instance, let’s revisit the online shopping system where we are selling three types of items: MP3s, DVDs and Books. The MP3 class, DVD class and Book class inherit from the parent Item class. Objects will be instantiated from the MP3 class, DVD class or Book class. We will never instantiate an object directly from the Item class. This is why the Item class is called an abstract class.
So to sum up, an abstract class:
- Cannot be instantiated directly.
- Can only be used through inheritance.
Object Oriented Analysis & Design
When designing a program based on procedural programming, we can use four main tools:
- Top Down Modular Design (Decomposition)
- Flowcharts
- Data Dictionary
- Test Plans
These tools are still very relevant when designing OOP programs. However additional tools can be used to identify the various classes/objects needed, their attributes and behaviours, the relationships between classes (inheritance) and how the various objects will interact with each other. One of the most widely used tool is UML, a standard used to draw a range of diagrams to visualise and design a system based on an OOP approach.
Click on the following banner to learn more about UML: