OOP Principles: Encapsulation Inheritance Polymorphism

A-Level Computer Science · Programming

OOP Principles: Encapsulation, Inheritance, and Polymorphism

Object-Oriented Programming (OOP) organises code around objects — self-contained units that bundle data (attributes) and behaviour (methods). The four pillars of OOP are encapsulation, inheritance, polymorphism, and abstraction.

Classes and Objects

A class is a blueprint; an object (instance) is a specific example built from that blueprint.

class Dog:
    def __init__(self, name, breed):
        self.__name = name      # private attribute
        self.__breed = breed    # private attribute

    def bark(self):
        return f"{self.__name} says Woof!"

    def get_name(self):
        return self.__name

fido = Dog("Fido", "Labrador")  # object/instance

Encapsulation

Encapsulation means bundling data and methods together, and hiding internal state from outside access. External code interacts only through the object's public interface (methods).

Why encapsulate?

  • Data protection: Prevents invalid states (e.g., a negative age)
  • Modularity: Changes to internal implementation don't break external code
  • Maintainability: Clear boundaries between components

Access modifiers:

ModifierPython conventionJava/C# keywordVisibility
Publicself.namepublicAccessible everywhere
Privateself.__nameprivateOnly within the class
Protectedself._nameprotectedWithin the class and subclasses

Getters and setters (accessor and mutator methods) provide controlled access:

class BankAccount:
    def __init__(self, balance):
        self.__balance = balance  # private

    def get_balance(self):        # getter
        return self.__balance

    def deposit(self, amount):    # setter with validation
        if amount > 0:
            self.__balance += amount
        else:
            raise ValueError("Deposit must be positive")

Inheritance

Inheritance allows a new class (subclass/child) to reuse the attributes and methods of an existing class (superclass/parent), then extend or modify them.

class Animal:                    # superclass
    def __init__(self, name):
        self._name = name

    def speak(self):
        return "..."

class Cat(Animal):               # subclass inherits from Animal
    def speak(self):             # overrides parent method
        return f"{self._name} says Meow!"

class Dog(Animal):
    def speak(self):
        return f"{self._name} says Woof!"

    def fetch(self):             # new method, only in Dog
        return f"{self._name} fetches the ball"

Key concepts:

  • The subclass inherits all public and protected attributes and methods
  • The subclass can override methods to provide specialised behaviour
  • The subclass can extend by adding new attributes and methods
  • Use super() to call the parent's constructor or methods

Benefits of inheritance:

  • Code reuse: Common code written once in the superclass
  • Hierarchy: Models real-world "is-a" relationships (a Cat IS AN Animal)
  • Extensibility: New subclasses can be added without changing existing code

Polymorphism

Polymorphism ("many forms") means the same method name behaves differently depending on the object's type. There are two forms:

1. Method overriding (runtime polymorphism):

The subclass provides its own implementation of a method inherited from the superclass.

animals = [Cat("Whiskers"), Dog("Rex"), Cat("Luna")]
for animal in animals:
    print(animal.speak())  # calls the correct version automatically

Output:

Whiskers says Meow!
Rex says Woof!
Luna says Meow!

The same .speak() call produces different output depending on the actual type — this is polymorphism.

2. Method overloading (compile-time polymorphism):

Multiple methods with the same name but different parameter lists (common in Java/C#, not natively supported in Python).

// Java example
int add(int a, int b) { return a + b; }
double add(double a, double b) { return a + b; }

Abstraction

Abstraction means hiding complex implementation details and showing only the essential features. In OOP, this is achieved through:

  • Abstract classes: Cannot be instantiated; define a template for subclasses
  • Abstract methods: Declared but not implemented in the abstract class; subclasses MUST implement them
  • Interfaces: Define a contract of methods a class must implement (Java)
from abc import ABC, abstractmethod

class Shape(ABC):               # abstract class
    @abstractmethod
    def area(self):             # abstract method - no implementation
        pass

    @abstractmethod
    def perimeter(self):
        pass

class Circle(Shape):            # must implement ALL abstract methods
    def __init__(self, radius):
        self.__radius = radius

    def area(self):
        return 3.14159 * self.__radius ** 2

    def perimeter(self):
        return 2 * 3.14159 * self.__radius

Composition vs Inheritance

Composition ("has-a") is an alternative to inheritance ("is-a"):

class Engine:
    def start(self):
        return "Engine running"

class Car:
    def __init__(self):
        self.engine = Engine()  # Car HAS an Engine (composition)

    def drive(self):
        return self.engine.start() + " - driving"

Use inheritance when there is a genuine "is-a" relationship. Use composition when one object contains or uses another.

Design Principles

PrincipleMeaning
Single ResponsibilityEach class should do one thing well
Open/ClosedOpen for extension, closed for modification
Liskov SubstitutionSubclass objects should work wherever parent objects are expected
DRYDon't Repeat Yourself — use inheritance/composition to eliminate duplication

Exam Tips

  • Define each OOP term with a clear one-sentence definition then give a code example
  • Encapsulation = data hiding + public interface. Don't just say "bundling data and methods"
  • Inheritance models "is-a"; composition models "has-a" — know when each is appropriate
  • Polymorphism means the same method call behaves differently for different types — always illustrate with a loop over a list of mixed objects
  • In pseudocode questions, show constructors, method definitions, and super() calls clearly
  • Abstract classes cannot be instantiated — this is a common exam trap
Don't understand a part?

Sign in and ask our AI tutor to explain any passage in plain English.

Try AI explanations →

More on Programming

Recursion Object-Oriented Programming Programming Paradigms Functional Programming

← All A-Level Computer Science notes