Object-Oriented Programming

A Simple Guide for Beginners in Kenya

Introduction to OOP

Karibu! Welcome to the world of Object-Oriented Programming (OOP)!

If you're a beginner programmer in Kenya looking to understand how modern software is built, you're in the right place. OOP is a programming style that helps organize code in a way that mirrors real-world objects.

Think of it like organizing a market in Nairobi - each stall (object) has its own products (data) and ways of doing business (methods)!

Kenyan software developers working together

Key OOP Concepts

OOP Concepts Diagram

OOP is built on several important concepts that work together to create powerful, organized code. Let's explore each one with examples from everyday Kenyan life!

1. Classes and Objects

A class is like a blueprint for creating objects. It defines what properties (data) and behaviors (methods) the objects will have.

An object is an instance of a class - a real thing created from the blueprint.

Kenyan Example: Think of a class as the blueprint for a matatu (minibus). The blueprint defines that matatus have a certain number of seats, a specific route, and can perform actions like "drive" and "collect fare". Each actual matatu on the road is an object - a real instance of that blueprint.


# Class definition
class Matatu:
    def __init__(self, route, seats, reg_number):
        self.route = route
        self.seats = seats
        self.reg_number = reg_number
        self.passengers = 0

    def board_passenger(self):
        if self.passengers < self.seats:
            self.passengers += 1
            return True
        return False

    def collect_fare(self, amount):
        return f"Collected KSh {amount} fare"

# Creating objects (instances)
city_hopper_matatu = Matatu("Nairobi-Westlands", 14, "KBZ 123C")
mombasa_express_matatu = Matatu("Mombasa-Malindi", 25, "KCY 456D")

print(city_hopper_matatu.route)  # "Nairobi-Westlands"
city_hopper_matatu.board_passenger()
print(city_hopper_matatu.passengers)  # 1
print(city_hopper_matatu.collect_fare(70))  # "Collected KSh 70 fare"
                        

2. Inheritance

Inheritance allows a class to inherit properties and methods from another class. The class that inherits is called a child class or subclass, and the class it inherits from is the parent class or superclass.

Kenyan Example: In Kenya, we have different types of public transport vehicles - matatus, buses, and tuk-tuks. They all share some common features (they transport people and charge fares), but each has unique characteristics too.

Inheritance Diagram

# Parent class
class PublicTransport:
    def __init__(self, reg_number, capacity):
        self.reg_number = reg_number
        self.capacity = capacity
        self.passengers = 0

    def board_passenger(self):
        if self.passengers < self.capacity:
            self.passengers += 1
            return True
        return False

    def collect_fare(self, amount):
        return f"Collected KSh {amount} fare"


# Child class inheriting from PublicTransport
class Matatu(PublicTransport):
    def __init__(self, reg_number, capacity, route):
        # Call parent constructor
        super().__init__(reg_number, capacity)
        self.route = route
        self.has_music = True

    def play_music(self):
        return "Playing the latest Kenyan hits!"


# Another child class
class TukTuk(PublicTransport):
    def __init__(self, reg_number):
        # Tuk-tuks always have 3 seats
        super().__init__(reg_number, 3)
        self.wheel_count = 3

    def navigate_narrow_street(self):
        return "Easily navigating through narrow streets!"


city_matatu = Matatu("KBZ 123C", 14, "Nairobi-Westlands")
print(city_matatu.collect_fare(50))  # From parent: "Collected KSh 50 fare"
print(city_matatu.play_music())  # From child: "Playing the latest Kenyan hits!"

mombasa_tuktuk = TukTuk("KCY 789E")
print(mombasa_tuktuk.capacity)  # 3
print(mombasa_tuktuk.navigate_narrow_street())  # "Easily navigating through narrow streets!"
                    

3. Encapsulation

Encapsulation means bundling data (properties) and the methods that work on that data within a single unit (class), and restricting direct access to some of the object's components.

Kenyan Example: Think of M-Pesa, Kenya's mobile money service. You don't need to know how it works internally - you just use the interface (send money, receive money, check balance). The complex details are hidden inside.

Encapsulation visualization

class MPesaAccount:
    def __init__(self, phone_number, name):
        self.phone_number = phone_number
        self.name = name
        self._balance = 0  # Private property (by convention)
        self._pin = "0000"  # Private property
        self._transaction_history = []  # Private property

    # Public methods (interface)
    def check_balance(self, entered_pin):
        if self._validate_pin(entered_pin):
            return f"Your balance is KSh {self._balance}"
        return "Incorrect PIN"

    def deposit(self, amount):
        self._balance += amount
        self._record_transaction("deposit", amount)
        return f"Deposited KSh {amount}. New balance: KSh {self._balance}"

    def withdraw(self, amount, entered_pin):
        if not self._validate_pin(entered_pin):
            return "Incorrect PIN"

        if amount > self._balance:
            return "Insufficient funds"

        self._balance -= amount
        self._record_transaction("withdrawal", amount)
        return f"Withdrawn KSh {amount}. New balance: KSh {self._balance}"

    def change_pin(self, old_pin, new_pin):
        if self._validate_pin(old_pin):
            self._pin = new_pin
            return "PIN changed successfully"
        return "Incorrect PIN"

    # Private methods (internal implementation)
    def _validate_pin(self, entered_pin):
        return self._pin == entered_pin

    def _record_transaction(self, transaction_type, amount):
        transaction = {
            "type": transaction_type,
            "amount": amount,
            "date": datetime.now()
        }
        self._transaction_history.append(transaction)


# Example usage
from datetime import datetime

john_account = MPesaAccount("0712345678", "John Kamau")
print(john_account.deposit(1000))  # Deposited KSh 1000. New balance: KSh 1000
print(john_account.check_balance("0000"))  # Your balance is KSh 1000
print(john_account.withdraw(500, "0000"))  # Withdrawn KSh 500. New balance: KSh 500

# Direct access to private properties is discouraged
# print(john_account._balance)  # Not recommended
# john_account._pin = "1234"  # Not recommended
                    

4. Polymorphism

Polymorphism means "many forms" and allows objects of different classes to be treated as objects of a common superclass. The most common use is when a parent class reference is used to refer to a child class object.

Kenyan Example: In Kenya, we have many languages, but we all communicate. Whether someone speaks Swahili, English, Kikuyu, or Luo, they can all respond to "Habari?" (How are you?) - but they might respond differently based on their culture and language.

Real-Life Example: A real-life example of polymorphism is any person who’s having many different characteristics. Like an employee at office, a husband and a father at home will have different behaviour everywhere.

Polymorphism visualization

# Parent class
class KenyanGreeter:
    def greet(self):
        return "Hello!"
    
    def farewell(self):
        return "Goodbye!"

# Child classes with their own implementations
class SwahiliGreeter(KenyanGreeter):
    def greet(self):
        return "Jambo!"
    
    def farewell(self):
        return "Kwaheri!"

class LuoGreeter(KenyanGreeter):
    def greet(self):
        return "Misawa!"
    
    def farewell(self):
        return "Oriti!"

class KikuyuGreeter(KenyanGreeter):
    def greet(self):
        return "Wĩmwega!"
    
    def farewell(self):
        return "Tigwo wega!"

# Polymorphism in action
def communicate_with_person(greeter):
    # We can call greet() and farewell() without knowing the specific type
    print(greeter.greet())
    print(greeter.farewell())

# Different objects, same interface
swahili_person = SwahiliGreeter()
luo_person = LuoGreeter()
kikuyu_person = KikuyuGreeter()

communicate_with_person(swahili_person)
# Output:
# "Jambo!"
# "Kwaheri!"

communicate_with_person(luo_person)
# Output:
# "Misawa!"
# "Oriti!"

communicate_with_person(kikuyu_person)
# Output:
# "Wĩmwega!"
# "Tigwo wega!"
                    

5. Abstraction

Abstraction means hiding complex implementation details and showing only the necessary features of an object.

Kenyan Example 1: When you use a Safaricom SIM card, you don't need to know how the cellular network works - you just need to know how to make calls, send texts, and use data. The complex details are abstracted away.

Kenyan Example 2: In Kenya's banking sector, ATMs provide a simple interface for withdrawing cash, checking balances, or depositing money. The internal workings of the ATM and the banking system are abstracted from the user.

Kenyan Example 3: M-Pesa agents provide a simple interface for sending and withdrawing money. Users don't need to understand the backend systems that process the transactions; they just interact with the agent or the mobile app.

Kenyan Example 4: In Kenya's matatu system, passengers only need to know their destination and fare. The logistics of route planning, fuel management, and vehicle maintenance are abstracted away from the passengers.

Abstraction visualization

# Abstract class
from abc import ABC, abstractmethod

class MobilePhone(ABC):
    def __init__(self, number):
        self.number = number

    @abstractmethod
    def make_call(self, recipient):
        pass

    @abstractmethod
    def send_text(self, recipient, message):
        pass

    def check_number(self):
        return f"Your phone number is {self.number}"

# Concrete implementation
class SafaricomPhone(MobilePhone):
    def __init__(self, number, mpesa_enabled=True):
        super().__init__(number)
        self.provider = "Safaricom"
        self.mpesa_enabled = mpesa_enabled

    def make_call(self, recipient):
        return f"Calling {recipient} using Safaricom network..."

    def send_text(self, recipient, message):
        return f'Text sent to {recipient}: "{message}" via Safaricom'

    def use_mpesa(self):
        if self.mpesa_enabled:
            return "Accessing M-Pesa services..."
        return "M-Pesa not enabled on this line"

# Another concrete implementation
class AirtelPhone(MobilePhone):
    def __init__(self, number):
        super().__init__(number)
        self.provider = "Airtel"

    def make_call(self, recipient):
        return f"Calling {recipient} using Airtel network..."

    def send_text(self, recipient, message):
        return f'Text sent to {recipient}: "{message}" via Airtel'

    def use_airtel_money(self):
        return "Accessing Airtel Money services..."

# Using the classes
# abstract_phone = MobilePhone("07123")  # Error: Can't instantiate abstract class

safaricom_phone = SafaricomPhone("0722123456")
print(safaricom_phone.check_number())  # "Your phone number is 0722123456"
print(safaricom_phone.make_call("0733987654"))  # "Calling 0733987654 using Safaricom network..."
print(safaricom_phone.use_mpesa())  # "Accessing M-Pesa services..."

airtel_phone = AirtelPhone("0733123456")
print(airtel_phone.send_text("0722987654", "Hello from Airtel"))  # 'Text sent to 0722987654: "Hello from Airtel" via Airtel'
                    

Test Your Knowledge

Now that you've learned about the key OOP concepts, test your understanding with these interactive quizzes!

Choose a Quiz

Select one of the quizzes below to test your knowledge of a specific OOP concept:

Quiz Title

Quiz description goes here.

Question 1 of 5

Question text goes here?

Explanation will appear here after answering.

Quiz Results

You scored 0 out of 5

Result message based on score

Real-World Examples

Building a Simple Safari Tour Booking System

Let's build a simple system that a Kenyan tour company might use to manage safari bookings:


from datetime import datetime

# Base class for all tours
class SafariTour:
    def __init__(self, name, duration, price):
        self.name = name
        self.duration = duration  # in days
        self.price = price  # in KSh
        self.booked_dates = []
        self.customers = []

    def get_info(self):
        return f"{self.name}: {self.duration} days for KSh {self.price}"

    def book(self, customer, date):
        # Check if date is available
        if self.is_date_available(date):
            self.booked_dates.append(date)
            self.customers.append(customer)
            return f"Booking confirmed for {customer.name} on {date.strftime('%Y-%m-%d')}"
        return f"Sorry, {self.name} is not available on {date.strftime('%Y-%m-%d')}"

    def is_date_available(self, date):
        # Check if the date is already booked
        return date not in self.booked_dates


# Different types of safaris
class WildlifeSafari(SafariTour):
    def __init__(self, name, duration, price, animals):
        super().__init__(name, duration, price)
        self.animals = animals  # List of animals likely to be seen

    def get_info(self):
        return f"{super().get_info()}. Animals you might see: {', '.join(self.animals)}"


class CulturalSafari(SafariTour):
    def __init__(self, name, duration, price, tribes):
        super().__init__(name, duration, price)
        self.tribes = tribes  # List of tribes to visit

    def get_info(self):
        return f"{super().get_info()}. Experience the culture of: {', '.join(self.tribes)}"

    def includes_ceremony(self):
        return self.duration >= 3  # Cultural ceremonies included for tours 3+ days


# Customer class
class Customer:
    def __init__(self, name, email, phone):
        self.name = name
        self.email = email
        self.phone = phone
        self.bookings = []

    def add_booking(self, tour, date):
        self.bookings.append({"tour": tour, "date": date})

    def get_booking_summary(self):
        if not self.bookings:
            return f"{self.name} has no bookings"

        summary = f"{self.name}'s bookings:\n"
        for index, booking in enumerate(self.bookings, start=1):
            summary += f"{index}. {booking['tour'].name} on {booking['date'].strftime('%Y-%m-%d')}\n"
        return summary


# Tour company class
class KenyanSafariCompany:
    def __init__(self, name):
        self.name = name
        self.tours = []
        self.revenue = 0

    def add_tour(self, tour):
        self.tours.append(tour)

    def book_tour(self, tour_name, customer, date):
        tour = next((t for t in self.tours if t.name == tour_name), None)
        if not tour:
            return f'Tour "{tour_name}" not found'

        booking_result = tour.book(customer, date)
        if not booking_result.startswith("Sorry"):
            # Booking was successful
            customer.add_booking(tour, date)
            self.revenue += tour.price
        return booking_result

    def get_total_revenue(self):
        return f"Total revenue: KSh {self.revenue}"

    def get_available_tours(self):
        return "\n".join(tour.get_info() for tour in self.tours)


# Usage example
from datetime import datetime  # Ensure datetime is imported

maasai_mara_safari = WildlifeSafari(
    "Maasai Mara Adventure",
    4,
    25000,
    ["Lions", "Elephants", "Giraffes", "Zebras", "Wildebeest"]
)

samburu_cultural_tour = CulturalSafari(
    "Samburu Cultural Experience",
    3,
    18000,
    ["Samburu", "Turkana"]
)

nairobi_national_park = WildlifeSafari(
    "Nairobi National Park Day Trip",
    1,
    5000,
    ["Rhinos", "Lions", "Buffaloes", "Giraffes"]
)

# Create a tour company
kenya_safaris = KenyanSafariCompany("Kenya Safaris Ltd")
kenya_safaris.add_tour(maasai_mara_safari)
kenya_safaris.add_tour(samburu_cultural_tour)
kenya_safaris.add_tour(nairobi_national_park)

# Create customers
john_kamau = Customer("John Kamau", "john@example.com", "0722123456")
mary_ochieng = Customer("Mary Ochieng", "mary@example.com", "0733987654")

# Book tours
print(kenya_safaris.book_tour("Maasai Mara Adventure", john_kamau, datetime(2023, 6, 15)))
print(kenya_safaris.book_tour("Samburu Cultural Experience", mary_ochieng, datetime(2023, 7, 20)))
print(kenya_safaris.book_tour("Nairobi National Park Day Trip", john_kamau, datetime(2023, 5, 10)))

# Check bookings and revenue
print(john_kamau.get_booking_summary())
print(mary_ochieng.get_booking_summary())
print(kenya_safaris.get_total_revenue())
                    

OOP in the Kenyan Context

Object-Oriented Programming is particularly relevant in Kenya's growing tech ecosystem. Here's why:

Mobile Money Revolution

Kenya's M-Pesa revolutionized mobile money globally. The system is built using OOP principles, with classes for users, transactions, agents, and more.

Tech Hubs and Startups

Nairobi's "Silicon Savannah" is home to numerous tech startups that use OOP to build scalable solutions for local challenges in agriculture, healthcare, and finance.

Education and Employment

Learning OOP opens doors to jobs in Kenya's growing software development sector, with companies like Safaricom, Andela, and international firms with local offices.

Learning Resources

Local Resources

  • Moringa School - Intensive coding bootcamps

Online Resources

  • Codecademy - Interactive OOP courses
  • freeCodeCamp - Free programming tutorials
  • Udemy - Affordable courses on OOP
  • YouTube - Free video tutorials