OOP

Object-Oriented Programming (OOP)

Understanding classes, objects, inheritance, polymorphism, and encapsulation

Learning Objectives

By the end of this lesson, you will be able to:

  • Understand OOP terminology including objects, properties, methods, classes, inheritance, polymorphism, encapsulation, getters, setters, and instances
  • Design appropriate classes to solve programming problems
  • Write code demonstrating the use of OOP concepts in both pseudocode and Python
  • Create classes with attributes and methods to represent real-world entities
  • Implement private attributes with getter and setter methods for encapsulation
  • Use constructors to initialize objects
  • Apply inheritance to create subclasses that inherit from parent classes
  • Implement polymorphism through method overriding
  • Combine text file processing with array and class objects
  • Understand containment aggregation relationships between classes

Key Terms

Class

A blueprint or template for creating objects

Object

An instance of a class with its own data and behavior

Property/Attribute

Data/variables that belong to a class or object

Method

Functions that belong to a class or object

Inheritance

A class inheriting properties and methods from another class

Polymorphism

Same method name behaving differently based on object type

Encapsulation

Bundling data with methods that operate on that data

Constructor

Special method called when an object is created

Getter/Setter

Methods to access and modify private attributes

Instance

A specific object created from a class

Containment/Aggregation

One class containing objects of another class

Access Modifiers

Keywords defining visibility of attributes/methods (public/private)

Introduction to OOP Basics

Object-Oriented Programming (OOP) is a way of writing programs by organizing the code into "objects." These objects represent things in the real world and combine attributes (data) and methods (actions). OOP makes programs easy to understand, reusable, and maintainable.

Real-Life Example: A Car

Think of a car as an object in OOP:

  • Class: Car (the blueprint)
  • Objects: Your specific car, your friend's car
  • Attributes: color = "Red", speed = 100 km/h
  • Methods: drive(), stop(), park()

Just like many cars can be made from the same blueprint, many objects can be created from the same class!

OOP Components

C
Class

Blueprint for creating objects (e.g., Car class)

O
Object

Instance of a class (e.g., myCar object)

A
Attributes

Data/characteristics of an object (e.g., color, speed)

M
Methods

Actions/functions of an object (e.g., drive(), stop())

Class Visualization: Car Class

Car Class
color
speed
brand
drive()
stop()
park()
Objects
myCar: color="Red", speed=100
friendCar: color="Blue", speed=120
familyCar: color="White", speed=80

How it works: The Car class is a blueprint. From this blueprint, we can create multiple objects (instances) like myCar, friendCar, etc. Each object has its own values for the attributes.

Writing a Simple Class

# Python Example: Simple Player Class
class Player:
def __init__(self):
self.__attempts = 0 # Private property
def set_attempts(self, number):
self.__attempts = number # Setter method
def get_attempts(self):
return self.__attempts # Getter method
# Main Program
player1 = Player()
player1.set_attempts(5)
print(player1.get_attempts()) # Outputs: 5
Output:
5

Access Modifiers: Public vs Private

Public
  • Can be accessed from anywhere
  • In Python: No special prefix
  • In Pseudocode: PUBLIC keyword
  • Example: Public methods that other code can call
Private
  • Only accessible inside the class
  • In Python: __ (double underscore prefix)
  • In Pseudocode: PRIVATE keyword
  • Example: Private attributes that need getters/setters

Activity 1: Create a Book Class

Create a class called Book with:

  • Attributes: title, author
  • Method: describe() that prints: "The book [title] is written by [author]"
  • Create two objects of the class and call the method for both

Write your solution in both Python and Pseudocode.

Solution:
Python:
class Book:
    def __init__(self, title, author):
        self.title = title
        self.author = author
    
    def describe(self):
        print(f"The book '{self.title}' is written by {self.author}.")

# Create objects
book1 = Book("1984", "George Orwell")
book2 = Book("To Kill a Mockingbird", "Harper Lee")

# Call method
book1.describe()
book2.describe()
Pseudocode:
CLASS Book
    PRIVATE Title : STRING
    PRIVATE Author : STRING
    
    PUBLIC PROCEDURE NEW(bookTitle : STRING, bookAuthor : STRING)
        Title ← bookTitle
        Author ← bookAuthor
    END PROCEDURE
    
    PUBLIC PROCEDURE Describe()
        OUTPUT "The book " & Title & " is written by " & Author
    END PROCEDURE
END CLASS

// Main Program
DECLARE book1, book2 : Book

book1 ← NEW Book("1984", "George Orwell")
book2 ← NEW Book("To Kill a Mockingbird", "Harper Lee")

book1.Describe()
book2.Describe()

Activity 2: Bank Account Management

Create a class named BankAccount with:

  • Attributes: AccountHolder (STRING), Balance (REAL), AccountNumber (STRING)
  • Method: Deposit() that increases the Balance by a specified amount
  • Test this with a bank account object by depositing $500
Solution:
Python:
class BankAccount:
    def __init__(self, account_holder, balance, account_number):
        self.AccountHolder = account_holder
        self.Balance = balance
        self.AccountNumber = account_number
    
    def Deposit(self, amount):
        self.Balance += amount
        print(f"${amount} deposited. New balance is ${self.Balance}.")

# Create object and test
account = BankAccount("John Doe", 1000.00, "123456789")
account.Deposit(500)  # Deposit $500
Pseudocode:
CLASS BankAccount
    PRIVATE AccountHolder : STRING
    PRIVATE Balance : REAL
    PRIVATE AccountNumber : STRING
    
    PUBLIC PROCEDURE NEW(holder : STRING, initialBalance : REAL, accNumber : STRING)
        AccountHolder ← holder
        Balance ← initialBalance
        AccountNumber ← accNumber
    END PROCEDURE
    
    PUBLIC PROCEDURE Deposit(amount : REAL)
        Balance ← Balance + amount
        OUTPUT "$" & amount & " deposited. New balance is $" & Balance
    END PROCEDURE
END CLASS

// Main Program
DECLARE account : BankAccount

account ← NEW BankAccount("John Doe", 1000.00, "123456789")
account.Deposit(500)  // Deposit $500

Check Your Understanding: OOP Basics

Answer
  • [1 mark] A class is like a blueprint or template for creating objects
  • [1 mark] It describes the properties (data) and methods (functions) that objects created from it will have
  • [Additional] For example, a Player class defines what properties (name, score) and methods (setScore, getScore) all Player objects will have
Answer
  • [1 mark] A class is the blueprint or template (e.g., Car class)
  • [1 mark] An object is an instance created from that class (e.g., myCar, yourCar)
  • [Additional] You can create many objects from a single class, just like you can build many houses from the same architectural blueprint
Answer
  • [1 mark] A getter method retrieves/returns the value of a private property
  • [1 mark] A setter method changes/sets the value of a private property
  • [Additional] They provide controlled access to private attributes, allowing validation or processing when getting/setting values
Answer
  • [1 mark] Public: Can be accessed from anywhere in the program
  • [1 mark] Private: Only accessible inside the class where they are declared
  • [1 mark] In Python, private attributes use __ (double underscore) prefix; in pseudocode, use PRIVATE keyword
Answer
  • [1 mark] Encapsulation: Protects data from being modified directly from outside the class
  • [1 mark] Allows validation/processing when getting or setting values (e.g., checking if age is positive)
  • [Additional] Makes code more maintainable - if we need to change how a value is stored, we only change the getter/setter methods
Answer
CLASS Car
    PRIVATE Brand : STRING
    PRIVATE Model : STRING
    
    PUBLIC PROCEDURE SetDetails(carBrand : STRING, carModel : STRING)
        Brand ← carBrand
        Model ← carModel
    END PROCEDURE
    
    PUBLIC FUNCTION GetDetails() RETURNS STRING
        RETURN Brand & " " & Model
    END FUNCTION
END CLASS

// Main Program
DECLARE car1 : Car

car1 ← NEW Car()
car1.SetDetails("Toyota", "Corolla")
OUTPUT car1.GetDetails()  // Outputs: Toyota Corolla

Constructors and Inheritance

Constructors are special methods that initialize objects when they are created. Inheritance allows classes to inherit properties and methods from other classes, promoting code reuse and creating hierarchical relationships.

Constructors

What is a Constructor?

A constructor is a special method automatically called when an object is created. It initializes the object's properties.

  • Pseudocode: Always named NEW
  • Python: Special method named __init__
  • Ensures object properties are set up when created
  • Can accept parameters to initialize with specific values

Constructor Example

Python:
class Player:
    def __init__(self, name, score):
        self.__name = name
        self.__score = score

player1 = Player("Alice", 100)
Pseudocode:
CLASS Player
    PUBLIC PROCEDURE NEW(name, score)
        // Initialize properties
    END PROCEDURE
END CLASS

DECLARE player1 : Player
player1 ← NEW Player("Alice", 100)

Inheritance

Animal (Parent)
name
species
speak()
eat()
Dog (Child)
Inherits: name, species
breed
Inherits: eat()
speak() // Overridden
bark()

Why Use Inheritance?

  • Code Reuse: Write once in parent class, use in child classes
  • Organized Code: Group similar things together
  • Flexibility: Add new features to child classes without affecting parent
  • Real Example: Animal → Dog, Cat, Bird (all share basic animal properties)

Inheritance Syntax

Python:
class Dog(Animal):
    # Dog inherits from Animal
    def bark(self):
        print("Woof!")
Pseudocode:
CLASS Dog INHERITS Animal
    // Dog inherits from Animal
    PUBLIC PROCEDURE Bark()
        OUTPUT "Woof!"
    END PROCEDURE
END CLASS

Using SUPER to Call Parent Constructor

When a child class needs to initialize properties from the parent class, use SUPER to call the parent constructor:

# Python: Using super()
class Pet:
def __init__(self, name):
self.__name = name
class Dog(Pet):
def __init__(self, name, breed):
super().__init__(name) # Call parent constructor
self.__breed = breed
// Pseudocode: Using SUPER
CLASS Pet
PUBLIC PROCEDURE NEW(petName)
Name ← petName
END PROCEDURE
END CLASS
CLASS Dog INHERITS Pet
PUBLIC PROCEDURE NEW(dogName, dogBreed)
SUPER.NEW(dogName) // Call parent constructor
Breed ← dogBreed
END PROCEDURE
END CLASS

Activity 3: Animal Shelter System

Create an animal shelter system with inheritance:

  1. Parent Class: Animal with properties: name, age and methods: eat(), sleep()
  2. Child Class: Dog that inherits from Animal with additional property: breed and additional method: bark()
  3. Create objects for both classes and call their methods
Solution:
Python:
class Animal:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    
    def eat(self):
        print(f"{self.name} is eating.")
    
    def sleep(self):
        print(f"{self.name} is sleeping.")

class Dog(Animal):
    def __init__(self, name, age, breed):
        super().__init__(name, age)
        self.breed = breed
    
    def bark(self):
        print(f"{self.name}, the {self.breed}, is barking!")

# Create objects
cat = Animal("Whiskers", 3)
dog = Dog("Buddy", 5, "Golden Retriever")

cat.eat()
cat.sleep()
dog.eat()  # Inherited from Animal
dog.bark()  # Specific to Dog
Pseudocode:
CLASS Animal
    PRIVATE Name : STRING
    PRIVATE Age : INTEGER
    
    PUBLIC PROCEDURE NEW(animalName : STRING, animalAge : INTEGER)
        Name ← animalName
        Age ← animalAge
    END PROCEDURE
    
    PUBLIC PROCEDURE Eat()
        OUTPUT Name & " is eating."
    END PROCEDURE
    
    PUBLIC PROCEDURE Sleep()
        OUTPUT Name & " is sleeping."
    END PROCEDURE
END CLASS

CLASS Dog INHERITS Animal
    PRIVATE Breed : STRING
    
    PUBLIC PROCEDURE NEW(dogName : STRING, dogAge : INTEGER, dogBreed : STRING)
        SUPER.NEW(dogName, dogAge)  // Call parent constructor
        Breed ← dogBreed
    END PROCEDURE
    
    PUBLIC PROCEDURE Bark()
        OUTPUT Name & ", the " & Breed & ", is barking!"
    END PROCEDURE
END CLASS

// Main Program
DECLARE cat : Animal
DECLARE dog : Dog

cat ← NEW Animal("Whiskers", 3)
dog ← NEW Dog("Buddy", 5, "Golden Retriever")

cat.Eat()
cat.Sleep()
dog.Eat()  // Inherited from Animal
dog.Bark()  // Specific to Dog

Check Your Understanding: Constructors & Inheritance

Answer
  • [1 mark] A constructor is a special method that initializes an object's properties
  • [1 mark] It is automatically called when an object is created (instantiated)
  • [Additional] In pseudocode it's named NEW, in Python it's __init__
Answer
  • [1 mark] Inheritance allows one class (child/subclass) to inherit properties and methods from another class (parent/superclass)
  • [1 mark] Promotes code reuse - write once in parent, use in multiple children
  • [1 mark] Creates hierarchical relationships (e.g., Animal → Dog, Cat, Bird)
Answer
  • [1 mark] To call the parent class's constructor from the child class
  • [1 mark] To ensure parent class properties are properly initialized before adding child-specific properties
  • [Additional] Also used to call parent class methods that have been overridden in the child class
Answer
// Pseudocode solution
CLASS Vehicle
    PRIVATE Brand : STRING
    PRIVATE Year : INTEGER
    
    PUBLIC PROCEDURE NEW(vehicleBrand : STRING, vehicleYear : INTEGER)
        Brand ← vehicleBrand
        Year ← vehicleYear
    END PROCEDURE
    
    PUBLIC FUNCTION GetDetails() RETURNS STRING
        RETURN Brand & " (" & Year & ")"
    END FUNCTION
END CLASS

CLASS Car INHERITS Vehicle
    PRIVATE Seats : INTEGER
    
    PUBLIC PROCEDURE NEW(carBrand : STRING, carYear : INTEGER, carSeats : INTEGER)
        SUPER.NEW(carBrand, carYear)  // Call parent constructor
        Seats ← carSeats
    END PROCEDURE
    
    PUBLIC FUNCTION GetDetails() RETURNS STRING
        RETURN SUPER.GetDetails() & " with " & Seats & " seats"
    END FUNCTION
END CLASS

// Python solution would be similar with:
// class Car(Vehicle):
//     def __init__(self, brand, year, seats):
//         super().__init__(brand, year)
//         self.__seats = seats

Polymorphism and Advanced Concepts

Polymorphism means "many forms." It allows the same method name to behave differently based on the object that calls it. With polymorphism, you can call the same method on objects of different classes, and each class can provide its own implementation.

Real-Life Example: Animal Sounds

Different animals make different sounds, but they all have a "speak" method:

  • Dog.speak() → "Woof Woof"
  • Cat.speak() → "Meow"
  • Bird.speak() → "Chirp Chirp"
  • Cow.speak() → "Moo"

Same method name (speak()), different implementations for each animal class!

Key Points of Polymorphism

1
Method Overriding

Subclass provides specific implementation of method from parent class

2
Shared Interface

Treat objects of different classes as objects of common parent class

3
Flexibility

Code works with objects of multiple types in unified way

Polymorphism Simulation: Animal Sounds

# Python: Polymorphism with method overriding
class Animal:
def speak(self):
print("Generic animal sound")
class Dog(Animal):
def speak(self): # Override parent method
print("Woof Woof")
class Cat(Animal):
def speak(self): # Override parent method
print("Meow")
# Create array of animals
animals = [Dog(), Cat(), Dog()]
# Polymorphism in action
for animal in animals:
animal.speak() # Calls appropriate method for each object
Output:
Woof Woof
Meow
Woof Woof

How it works: Even though all objects are stored in an Animal array, when animal.speak() is called, Python checks the actual object type at runtime and calls the appropriate method. This is polymorphism in action!

Activity 4: Vehicle System with Polymorphism

Create a Vehicle system demonstrating polymorphism:

  1. Create a Vehicle parent class with method get_details()
  2. Create Car and Truck subclasses that override get_details()
  3. Create a list of vehicles containing both Car and Truck objects
  4. Loop through the list and call get_details() on each
Solution:
Python:
class Vehicle:
    def get_details(self):
        return "Generic vehicle"

class Car(Vehicle):
    def get_details(self):  # Override
        return "Car: Passenger vehicle with 4-5 seats"

class Truck(Vehicle):
    def get_details(self):  # Override
        return "Truck: Heavy goods vehicle"

# Create list of vehicles
vehicles = [Car(), Truck(), Car()]

# Polymorphism in action
for vehicle in vehicles:
    print(vehicle.get_details())

# Output:
# Car: Passenger vehicle with 4-5 seats
# Truck: Heavy goods vehicle
# Car: Passenger vehicle with 4-5 seats
Pseudocode:
CLASS Vehicle
    PUBLIC FUNCTION GetDetails() RETURNS STRING
        RETURN "Generic vehicle"
    END FUNCTION
END CLASS

CLASS Car INHERITS Vehicle
    PUBLIC FUNCTION GetDetails() RETURNS STRING
        RETURN "Car: Passenger vehicle with 4-5 seats"
    END FUNCTION
END CLASS

CLASS Truck INHERITS Vehicle
    PUBLIC FUNCTION GetDetails() RETURNS STRING
        RETURN "Truck: Heavy goods vehicle"
    END FUNCTION
END CLASS

// Main Program
DECLARE vehicles : ARRAY OF Vehicle
DECLARE i : INTEGER

vehicles ← [NEW Car(), NEW Truck(), NEW Car()]

FOR i ← 0 TO LENGTH(vehicles) - 1
    OUTPUT vehicles[i].GetDetails()
NEXT i

// Output:
// Car: Passenger vehicle with 4-5 seats
// Truck: Heavy goods vehicle
// Car: Passenger vehicle with 4-5 seats

Check Your Understanding: Polymorphism

Answer
  • [1 mark] Polymorphism means "many forms"
  • [1 mark] It allows the same method name to behave differently based on the object that calls it
  • [Additional] Different classes can provide their own implementation of the same method
Answer
  • [1 mark] A subclass provides its own specific implementation of a method already defined in the parent class
  • [1 mark] The child class method "overrides" the parent class method
  • [Additional] Example: Dog.speak() overrides Animal.speak() with "Woof" instead of generic sound
Answer
  • [1 mark] You can store objects of different subclasses in an array of the parent class type
  • [1 mark] When looping through the array and calling a method, each object uses its own implementation
  • [1 mark] This allows writing generic code that works with multiple object types
  • [Additional] Example: Animal array with Dog, Cat objects - all can call speak() but produce different sounds

Key Takeaways

  • OOP organizes code into objects with attributes (data) and methods (actions)
  • A class is a blueprint for creating objects; objects are instances of classes
  • Encapsulation bundles data with methods and uses private attributes with getters/setters for controlled access
  • Constructors (__init__ in Python, NEW in pseudocode) initialize objects when created
  • Inheritance allows code reuse - child classes inherit from parent classes
  • Use SUPER to call parent constructors and methods from child classes
  • Polymorphism means "many forms" - same method name behaves differently based on object type
  • Method overriding allows subclasses to provide specific implementations of parent methods
  • Public attributes/methods are accessible from anywhere; private ones only within the class
  • OOP makes code more modular, reusable, and maintainable for complex programs
  • Real-world systems like banking, e-commerce, and games use OOP principles extensively

Question Bank

Marking Scheme & Answer
  • [2 marks] Encapsulation: Bundling data (attributes) with methods that operate on that data. Using private attributes with getters/setters for controlled access.
  • [2 marks] Inheritance: Creating new classes (child classes) that inherit properties and methods from existing classes (parent classes). Promotes code reuse.
  • [2 marks] Polymorphism: Same method name behaving differently based on object type. Method overriding in subclasses provides specific implementations.
  • [Additional] Classes and Objects: Classes are blueprints, objects are instances. Abstraction: Hiding complex implementation details.
Marking Scheme & Answer
Class:
  • Blueprint or template
  • Defines attributes and methods
  • Exists only once in memory
  • Example: Car class
Object:
  • Instance of a class
  • Has actual values for attributes
  • Multiple objects can exist
  • Example: myCar, yourCar objects
Relationship: A class is like a cookie cutter, objects are the cookies made from it.
Marking Scheme & Answer
  • [1 mark] Constructors are special methods called when an object is created
  • [1 mark] They initialize the object's properties/attributes
  • [1 mark] In Python: __init__ method; in Pseudocode: NEW procedure
  • [1 mark] Important because they ensure objects start with valid/initialized state
  • [Additional] Can accept parameters to initialize with specific values, can call parent constructors using super()
Marking Scheme & Answer
  • [1 mark] Inheritance allows a class (child) to inherit properties and methods from another class (parent)
  • [2 marks] Real-world example: Vehicle → Car, Truck, Motorcycle
    • Vehicle class has common properties: brand, model, year
    • Car inherits from Vehicle and adds: numberOfDoors
    • Truck inherits from Vehicle and adds: cargoCapacity
  • [1 mark] Promotes code reuse - common code in Vehicle, specific code in subclasses
  • [1 mark] Creates hierarchical relationships making code organized and maintainable
Marking Scheme & Answer
  • [1 mark] Polymorphism means "many forms" - same method name behaving differently based on object
  • [2 marks] Implementation through method overriding:
    • Parent class defines method
    • Child classes override with specific implementations
    • Example: Animal.speak() vs Dog.speak() vs Cat.speak()
  • [1 mark] Allows treating objects of different classes as objects of common parent class
  • [1 mark] Useful in arrays/loops - store different objects together, call same method on each
Marking Scheme & Answer
  • [1 mark] Getter methods retrieve values of private attributes; setter methods modify them
  • [1 mark] Provide controlled access to private data - can't be accessed/modified directly
  • [1 mark] Allow validation in setters (e.g., check if age is positive before setting)
  • [1 mark] Enable encapsulation - data bundled with methods, implementation details hidden
  • [Additional] Makes code more maintainable - if storage changes, only getter/setter needs updating