ResourcesComputer ScienceObject-Oriented Programming
Computer ScienceCollege

Object-Oriented Programming

Object-Oriented Programming (OOP) is a programming paradigm that organizes software design around objects — entities that bundle data (attributes) and behavior (methods) into self-contained units. It is the dominant paradigm in modern software engineering.

This guide covers classes and objects, the four pillars of OOP (encapsulation, inheritance, polymorphism, abstraction), SOLID design principles, composition vs aggregation, code walkthroughs, and a 10-question practice quiz.

Inheritance Chain: Object Creation & Method Resolution

// Click Play or Step Forward to begin

No classes defined yet

Step through to see how classes inherit attributes and methods in a chain.
Step 0 / 7
Watch how classes build on each other through inheritance. Child classes inherit attributes and methods from parent classes, and Python's MRO determines which method implementation is called.

1Introduction

Object-Oriented Programming (OOP) is a paradigm that models software as a collection of objects — self-contained units that encapsulate data and behavior. Rather than thinking of a program as a sequence of instructions, OOP encourages you to think in terms of real-world entities and their interactions.

OOP is central to Software Engineering (modular design, maintainability), Systems Design (modeling complex domains), Programming Languages (type hierarchies, dispatch mechanisms), and Design Patterns (reusable architectural solutions). Languages like Java, Python, C++, C#, and Swift are all built around OOP principles.

In Practice

Nearly every large-scale software system — from web frameworks (Django, Spring) to game engines (Unity, Unreal) to mobile apps (Android, iOS) — is built using OOP. Understanding these principles is essential for working on professional codebases and passing technical interviews.

Overview of the four pillars of object-oriented programming: encapsulation, inheritance, polymorphism, and abstraction

2Key Definitions

Essential terms for understanding object-oriented programming at the university level.

Class

A blueprint defining the attributes and methods that objects of that type will have

Object (Instance)

A concrete entity created from a class, with its own attribute values

Attribute (Field)

A variable belonging to an object that stores its state

Method

A function belonging to a class that defines behavior of its objects

Constructor

Special method called automatically to initialize a new object (__init__ in Python)

Destructor

Special method called when an object is destroyed to release resources (~ClassName in C++)

Encapsulation

Bundling data and methods together; restricting direct access to internal state

Inheritance

Child class inherits attributes and methods from a parent class (“is-a” relationship)

Polymorphism

One interface, many forms — different classes respond differently to the same method call

Abstraction

Hiding complexity; exposing only essential features via abstract classes/interfaces

Composition

Strong “has-a” relationship — part's lifecycle tied to the whole (Car has Engine)

Aggregation

Weak “has-a” relationship — part can exist independently (Department has Professors)

Access Modifier

Keywords controlling visibility: public, private, protected

Abstract Class

A class that cannot be instantiated; meant to be subclassed with abstract methods implemented

Interface

A contract specifying methods a class must implement (Java/C#; ABCs in Python)

Design Pattern

Reusable solution template for common software design problems (Singleton, Factory, Observer)

3Classes and Objects

At the heart of OOP are two intertwined concepts: classes (blueprints) and objects (instances). A class defines what an entity looks like and can do; an object is a concrete realization of that definition in memory.

Classes: The Blueprints

A class defines attributes (data members) that hold state and methods (member functions) that define behavior. Think of it as an architect's blueprint — it specifies rooms and doors but isn't a building itself.

Objects: The Instances

An object is created by instantiating a class. Each object has its own set of attribute values but shares the method definitions from its class. You can create many objects from a single class.

Class structure diagram showing attributes, methods, constructor, and object instantiation

Constructors, Methods, and Destructors

Constructor

Automatically called when an object is created. Initializes attributes to a valid state.

__init__(self) in Python

Method

A function belonging to a class that defines actions the object can perform.

def accelerate(self)

Destructor

Called when an object is destroyed. Releases resources like file handles.

__del__(self) / ~Class()

4Encapsulation

Encapsulation bundles data and the methods that operate on it within a single unit (the class), and hides internal state from direct external access. Interaction with an object's data happens only through its public methods.

Analogy

Think of a smartphone. You interact through the touchscreen and buttons (public methods). The internal circuits, battery, and processor (private data) are hidden behind the casing. You don't need to understand the internals to use the phone, and the manufacturer can change the internals without changing how you use it.

Access Modifiers

Public

Accessible from anywhere. Forms the object's external interface.

self.name

Protected

Accessible within the class and its subclasses.

self._name

Private

Accessible only within the class itself. Hidden from outside.

self.__name

Benefits of Encapsulation

  • Data protection: Prevents unauthorized modification of internal state
  • Modularity: Each object manages its own state independently
  • Flexibility: Internal implementation can change without affecting external code
  • Debugging: Errors are isolated within well-defined boundaries

5Inheritance

Inheritance allows a new class (child/subclass) to inherit attributes and methods from an existing class (parent/superclass). This establishes an “is-a” relationship and enables code reuse.

Inheritance hierarchy diagram showing parent and child class relationships

Types of Inheritance

Single Inheritance

A child class inherits from exactly one parent class. The simplest and most common form.

class Dog(Animal)

Multiple Inheritance

A child inherits from two or more parents. Supported in Python and C++, not Java.

class FlyingFish(Fish, Bird)

Multilevel Inheritance

A chain of inheritance: GuideDog extends Dog, which extends Animal.

Animal → Dog → GuideDog

Hierarchical Inheritance

Multiple child classes inherit from the same parent class.

Animal → Dog, Cat, Bird

Composition vs. Inheritance

Inheritance (“is-a”)

Child extends parent. Tighter coupling. Good for genuine type hierarchies.

A Dog is an Animal

Composition (“has-a”)

Object contains another object. Looser coupling. More flexible.

A Car has an Engine

6Polymorphism

Polymorphism (Greek: “many forms”) allows objects of different classes to be treated as objects of a common type. A single interface adapts its behavior depending on the actual object type.

Polymorphism diagram showing how different objects respond differently to the same method call

Method Overriding vs. Overloading

Method Overriding

Subclass provides a specific implementation of a method already defined in the parent class. Same name, same parameters.

Dog.speak() overrides Animal.speak()

Method Overloading

Multiple methods with the same name but different parameters. Common in Java/C++. Python uses default args instead.

add(int, int) vs add(float, float)

Analogy

Think of a “play” button on a remote control. Pressing it on a DVD player starts a movie. On a music player, it plays a song. On a game console, it launches a game. The button (interface) is the same, but the action (implementation) varies by device (object type).

7Abstraction

Abstraction focuses on exposing only the essential features of an object while hiding the complex implementation details. It answers “what does it do?” without requiring knowledge of “how does it work?”

Abstract Classes vs. Interfaces

Abstract Class

  • Cannot be instantiated directly
  • Can have both abstract and concrete methods
  • Can have instance variables
  • Single inheritance (Java) / multiple (Python/C++)

Interface

  • Defines a pure contract (method signatures only)
  • No implementation (except default methods in Java 8+)
  • No instance variables (only constants)
  • A class can implement multiple interfaces
Analogy

Driving a car is abstraction in action. You interact with the steering wheel, accelerator, and brake (the abstract interface). You don't need to understand the combustion engine, transmission gears, or hydraulic brake system. The complexity is abstracted away behind a simple interface.

8SOLID Principles

SOLID is an acronym for five design principles promoted by Robert C. Martin that make software more understandable, flexible, and maintainable.

SOLID principles diagram showing all five principles with examples

S — Single Responsibility Principle

A class should have only one reason to change. Each class handles one responsibility.

Example: A Report class generates data; a ReportPrinter class handles printing. Don't combine both.

O — Open/Closed Principle

Software entities should be open for extension but closed for modification.

Example: Add new Shape subclasses without modifying the core drawing logic.

L — Liskov Substitution Principle

Subtypes must be substitutable for their base types without altering program correctness.

Example: A function expecting Animal should work correctly with Dog, Cat, or any subtype.

I — Interface Segregation Principle

Clients should not be forced to depend on interfaces they don't use. Prefer small, specific interfaces.

Example: Split a monolithic Worker interface into IWorker, IEater, ISleeper. A Robot only implements IWorker.

D — Dependency Inversion Principle

Depend on abstractions, not concretions. High-level modules should not depend on low-level modules.

Example: PaymentProcessor depends on an IPaymentGateway interface, not directly on StripeApi.

9Code Walkthroughs

Introductory

Class Definition and Object Creation

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def greet(self):
        return f"Hi, I am {self.name}, age {self.age}"

# Create objects (instances)
p1 = Person("Alice", 25)
p2 = Person("Bob", 30)

print(p1.greet())  # Hi, I am Alice, age 25
print(p2.greet())  # Hi, I am Bob, age 30

Line 1: class Person: defines the blueprint with a name.

Lines 2-4: __init__ is the constructor — called automatically when creating an object. self refers to the instance being created.

Lines 10-11: Two separate objects are created, each with their own attribute values.

Time: O(1)Space: O(1)

Key insight: Classes define blueprints; objects are concrete instances with their own state.

Introductory

Encapsulation with Access Control

class BankAccount:
    def __init__(self, holder, balance=0):
        self.holder = holder        # public
        self.__balance = balance    # private (name mangling)

    def deposit(self, amount):
        if amount > 0:
            self.__balance += amount

    def get_balance(self):
        return self.__balance

acct = BankAccount("Alice", 1000)
acct.deposit(500)
print(acct.get_balance())    # 1500
# print(acct.__balance)      # AttributeError!

Line 4: __balance uses double underscore for name mangling — Python makes it harder to access directly.

Lines 6-8: deposit() is a public method that validates input before modifying private state.

Line 16: Direct access to __balance raises an error — data is protected.

Time: O(1)Space: O(1)

Key insight: Encapsulation protects internal state and forces interaction through controlled methods.

Intermediate

Inheritance and Method Overriding

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

    def speak(self):
        raise NotImplementedError("Subclass must implement")

    def eat(self):
        print(f"{self.name} is eating.")

class Dog(Animal):
    def __init__(self, name, breed):
        super().__init__(name)  # Call parent constructor
        self.breed = breed

    def speak(self):  # Override parent method
        print(f"{self.name} says Woof!")

class Cat(Animal):
    def speak(self):  # Override parent method
        print(f"{self.name} says Meow!")

dog = Dog("Buddy", "Golden Retriever")
dog.eat()    # Inherited from Animal
dog.speak()  # Overridden in Dog: "Buddy says Woof!"

Line 11: class Dog(Animal) — Dog inherits from Animal.

Line 13: super().__init__(name) calls the parent constructor to initialize inherited attributes.

Line 16: speak() is overridden — Dog provides its own implementation.

Line 24: eat() is inherited from Animal and used directly.

Time: O(1)Space: O(1)

Key insight: Inheritance allows code reuse; method overriding lets subclasses customize behavior.

Advanced

Polymorphism and Abstract Base Classes

from abc import ABC, abstractmethod

class Shape(ABC):
    @abstractmethod
    def area(self):
        pass

    @abstractmethod
    def perimeter(self):
        pass

class Circle(Shape):
    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

class Rectangle(Shape):
    def __init__(self, w, h):
        self.w, self.h = w, h

    def area(self):
        return self.w * self.h

    def perimeter(self):
        return 2 * (self.w + self.h)

# Polymorphism: same interface, different behavior
shapes = [Circle(5), Rectangle(4, 6)]
for s in shapes:
    print(f"Area: {s.area()}, Perimeter: {s.perimeter()}")

Lines 3-10: Shape(ABC) is abstract — cannot be instantiated. Forces subclasses to implement area() and perimeter().

Lines 33-35: Polymorphism in action — the loop treats all shapes uniformly through the common Shape interface.

Time: O(1) per shapeSpace: O(n) for list

Key insight: Abstract classes enforce a contract; polymorphism lets you write generic code that works with any subtype.

10Memory Aids

Class vs Object

“Class = cookie cutter (the mold). Object = cookie (the actual thing you eat). One cutter makes many cookies.”

Encapsulation

“A capsule (pill) wraps medicine inside a shell. Encapsulation wraps data inside a class, with methods as the controlled opening.”

Inheritance

“Children inherit traits from parents — eye color, height, behaviors. Child classes inherit attributes and methods from parent classes.”

Polymorphism

“A USB port is polymorphic — plug in a mouse, keyboard, or flash drive. Same interface, different devices, different behaviors.”

SOLID Mnemonic

“S-ingle job, O-pen door (but locked code), L-ike-for-like swaps, I-nterfaces stay slim, D-epend on blueprints not bricks.”

Composition vs Inheritance

“Inheritance = 'I am a...' (Dog is an Animal). Composition = 'I have a...' (Car has an Engine). When in doubt, prefer 'has-a'.”

11Common Mistakes

Using Inheritance When Composition Is Better

Mistake: Creating deep inheritance hierarchies for code reuse

Fix: Favor composition over inheritance. Use “has-a” relationships for flexibility. Inheritance creates tight coupling; composition keeps components independent and swappable.

Forgetting to Call super().__init__()

Mistake: class Dog(Animal): def __init__(self, breed): self.breed = breed

Fix: Always call super().__init__() in child constructors to initialize inherited attributes. Otherwise, parent attributes like name will not exist.

Breaking the Liskov Substitution Principle

Mistake: A Square class inheriting from Rectangle that breaks when width and height are set independently

Fix: Ensure subclasses can be used anywhere the parent is expected without breaking behavior. If a Square cannot honor Rectangle's contract, use composition or a shared interface instead.

God Classes (Violating SRP)

Mistake: A single class that handles user auth, database queries, email sending, and logging

Fix: Each class should have one responsibility. Split into UserAuthenticator, DatabaseService, EmailService, and Logger. This makes each class easier to test, maintain, and reuse.

Exposing Internal State Directly

Mistake: Making all attributes public and modifying them directly from outside

Fix: Use private attributes with getter/setter methods that include validation. This protects invariants — for example, a balance should never go negative without authorization.

Confusing Overriding with Overloading

Mistake: Thinking method overriding and overloading are the same concept

Fix: Overriding = same method signature in a subclass (runtime polymorphism). Overloading = same name but different parameters in the same class (compile-time polymorphism in Java/C++). Python doesn't support traditional overloading.

Using Mutable Default Arguments in Python

Mistake: def __init__(self, items=[]) — all instances share the same list!

Fix: Use def __init__(self, items=None): self.items = items or []. Default mutable arguments are created once and shared across all calls, causing subtle bugs.

Forgetting self in Method Definitions

Mistake: def greet(): return "Hello" inside a class — missing self

Fix: Instance methods must include self as the first parameter: def greet(self). Without it, Python cannot bind the method to the instance.

Frequently Asked Questions

What is the difference between a class and an object?
A class is a blueprint or template that defines attributes (data) and methods (behavior). An object is a concrete instance of a class — the actual entity created in memory with specific attribute values. You can create many objects from one class, each with its own state.
What is the difference between encapsulation and abstraction?
Encapsulation bundles data and methods together and restricts direct access to internal state using access modifiers (public, private, protected). Abstraction hides implementation complexity and exposes only essential features through abstract classes and interfaces. Encapsulation is about data protection; abstraction is about simplification.
When should I use inheritance vs. composition?
Use inheritance for "is-a" relationships (a Dog IS an Animal). Use composition for "has-a" relationships (a Car HAS an Engine). The general guideline is "favor composition over inheritance" because composition provides looser coupling, better flexibility, and avoids fragile base class problems.
What is method overriding vs. method overloading?
Method overriding: a subclass provides a specific implementation for a method already defined in its superclass (same name, same parameters). Method overloading: defining multiple methods with the same name but different parameter types or counts in the same class. Python uses default arguments and *args/**kwargs instead of traditional overloading.
What are the SOLID principles?
SOLID is an acronym for five design principles: Single Responsibility (one reason to change), Open/Closed (open for extension, closed for modification), Liskov Substitution (subtypes must be substitutable for base types), Interface Segregation (prefer specific interfaces over general ones), and Dependency Inversion (depend on abstractions, not concretions).
What is polymorphism and why is it useful?
Polymorphism ("many forms") allows objects of different classes to be treated through a common interface. A function that expects an Animal can work with a Dog, Cat, or any subtype. This eliminates long if-elif chains, makes code extensible (add new types without modifying existing code), and enables flexible, decoupled designs.

Practice Quiz

Test your understanding of object-oriented programming — select the correct answer for each question.

1.What is the primary role of a class in Object-Oriented Programming?

2.Which OOP principle involves bundling data and methods within a single unit and restricting direct access to some of the object's components?

3.If a SportsCar class extends a Car class, what kind of relationship does this represent?

4.Which concept allows objects of different classes to be treated as objects of a common type, often demonstrated through method overriding?

5.What is the purpose of an abstract method in an abstract class?

6.Which of the following best describes Composition?

7.According to the Single Responsibility Principle (SRP), a class should have:

8.What is a constructor primarily used for in OOP?

9.Which SOLID principle states that software entities should be open for extension but closed for modification?

10.What is the Liskov Substitution Principle (LSP)?

Study Tips

  • Model real-world objects: Pick everyday items (cars, bank accounts, animals) and practice defining them as classes with attributes and methods.
  • Draw class diagrams: Use UML class diagrams to visualize inheritance hierarchies, composition, and aggregation relationships before writing code.
  • Practice the four pillars independently: Write small programs focused on each pillar — one for encapsulation, one for inheritance, one for polymorphism, one for abstraction.
  • Refactor procedural code: Take a procedural program and refactor it into an OOP design. This builds intuition for when and how to use classes.
  • Apply SOLID to existing projects: Review your past code and identify violations of SOLID principles. Refactor to fix them.

Related Topics