Skip to content

Session 4: Object Model & Class FundamentalsΒΆ

Week: 4
Element: ICTPRG430 Element 2.1
Duration: 4 hours
Phase: Object-Oriented Programming Theory


πŸ“Š Lecture SlidesΒΆ

Week 4 Presentation Slides

View the interactive presentation slides for this session:

πŸ“Š Open Slides in New Window pptx slides

The slides cover: - Why OOP works (encapsulation, modularity, reusability) - Classes vs objects β€” blueprints and instances - UML class diagrams for BankAccount - Encapsulation with @property and @setter - Magic methods __str__ and __repr__ - Single Responsibility Principle - 7-step BankAccount implementation walkthrough


Session IntroductionΒΆ

In this session you will explore the foundational concepts of Object-Oriented Programming (OOP) through universal examples. You'll understand why OOP is essential for managing complexity, then apply those patterns to design and implement a RobotBase class in your assessment. This session emphasizes learning patterns through clear, relatable examples that you'll then apply independently.

Learning ObjectivesΒΆ

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

  • Understand fundamental OOP concepts: classes, objects, and object instantiation
  • Design a class with appropriate instance variables and methods
  • Explain encapsulation principles and their benefit for software design
  • Write getter and setter methods to safely access and modify object state
  • Use Python decorators (@property, @setter) to enforce method-based access
  • Implement magic methods (__str__, __repr__) for object representation
  • Apply single responsibility principle to class design
  • Apply learned patterns to implement a RobotBase class from a specification

Session StructureΒΆ

  1. Why OOP Works - Managing complexity, encapsulation, modularity, reusability
  2. Classes and Objects - Blueprints and instances
  3. Encapsulation Patterns - Safely access and modify instance variables
  4. @property and @setter Decorators - Python's elegant way to enforce methods
  5. Magic Methods - String representation (str, repr)
  6. Single Responsibility Principle - One method, one job
  7. 7-Step Implementation - Complete working class with all concepts
  8. Assessment - Apply patterns to RobotBase (separate, spec-based)

Pre-Session PreparationΒΆ

Required

  • Python 3.9+ installed
  • VS Code with Python extension
  • Basic familiarity with functions and data types

Review

  • Function definitions and parameters
  • Python dictionaries and lists
  • Basic testing concepts (from Sessions 1-3)

1. Why Object-Oriented Programming?ΒΆ

Software systems are complex with multiple components that must work together. OOP provides the structure needed to manage this complexity.

Key AdvantagesΒΆ

Encapsulation: Hide complexity behind clear interfaces - Example: Bank balance doesn't expose internal transaction log - Benefit: Change internal implementation without breaking client code

Modularity: Each component is independent - Example: Balance calculation, transaction methods, status reporting - Benefit: Test and update each component in isolation

Reusability: Design once, use everywhere - Example: Same patterns work for bank accounts, student records, inventory, robots - Benefit: Universal design patterns apply everywhere

Flexibility: Change behavior without touching other components - Example: Change how interest is calculated - Benefit: Updates don't break rest of system


2. Classes and Objects: The FoundationΒΆ

Understanding the RelationshipΒΆ

Class: A blueprint or template for creating objects - Like architectural plans for a building - Defines structure (instance variables) and capabilities (methods) - PascalCase naming: BankAccount, Student, Robot

Object (Instance): An individual "thing" created from a class - Like actual buildings constructed from plans - Each has unique state and identity - Multiple objects can come from one class: account1, account2, account3

Python Class BasicsΒΆ

A Python class defines a blueprint. Here's the essential pattern:

class BankAccount:
    """Blueprint for bank account objects."""

    def __init__(self, holder_name, account_type, balance):
        """Initialize instance variables (state)."""
        self.holder_name = holder_name
        self.account_type = account_type
        self.balance = balance

    def deposit(self, amount):
        """Method - what the object can do."""
        self.balance += amount

3. The Key: Understanding selfΒΆ

self is the reference to the specific object. It's how Python knows which object's data to modify.

# Create two separate objects
alice_account = BankAccount("Alice", "Savings", 1000)
bob_account = BankAccount("Bob", "Checking", 500)

# Each has independent state
alice_account.deposit(100)  # Changes alice_account's balance
bob_account.deposit(50)     # Changes bob_account's balance

# They don't affect each other
print(alice_account.balance)  # 1100
print(bob_account.balance)    # 550

Inside the deposit method: - When called on alice_account, self refers to alice_account - When called on bob_account, self refers to bob_account - Python automatically passes the correct object as self


4. Encapsulation: Protecting Your DataΒΆ

The Problem: Direct AccessΒΆ

Without encapsulation, anyone can modify state however they want:

account = BankAccount("Alice", "Savings", 1000)
account.balance = -1000000  # Oops! No validation!
account.balance = "rich"    # Type error!

The Solution: Private VariablesΒΆ

Use underscore prefix to signal "don't access directly":

class BankAccount:
    def __init__(self, holder_name, account_type, balance):
        self._holder_name = holder_name      # Private!
        self._account_type = account_type
        self._balance = balance

Then provide controlled access through methods:

def get_balance(self):
    """Getter - read access only."""
    return self._balance

def set_balance(self, value):
    """Setter - write access with validation."""
    if value < 0:
        raise ValueError("Balance cannot be negative")
    self._balance = value

5. Python's Elegant Solution: @propertyΒΆ

Instead of get_ and set_ methods, Python uses decorators:

class BankAccount:
    def __init__(self, holder_name, account_type, balance):
        self._balance = balance

    @property
    def balance(self):
        """Getter - called when you read the attribute."""
        return self._balance

    @balance.setter
    def balance(self, value):
        """Setter - called when you assign the attribute."""
        if value < 0:
            raise ValueError("Balance cannot be negative")
        self._balance = value

# Usage - looks like an attribute, but validated!
account = BankAccount("Alice", "Savings", 1000)
print(account.balance)      # Calls @property getter
account.balance = 500       # Calls @setter with validation
account.balance = -100      # ValueError! Protected!

Why @property is BetterΒΆ

  • Cleaner syntax: Looks like account.balance, not account.get_balance()
  • Protected: Validation happens automatically
  • Flexible: Can change from attribute to method later without breaking client code

6. Magic Methods: String RepresentationΒΆ

Magic methods (double underscore methods) are called automatically by Python.

__str__() - User-Friendly StringΒΆ

Called by print() and str(). Should be simple and readable:

def __str__(self):
    """Return string for print()."""
    return f"BankAccount({self._holder_name}, ${self._balance:.2f})"

# Usage
account = BankAccount("Alice", "Savings", 1500)
print(account)  # Calls __str__()
# Output: BankAccount(Alice, $1500.00)

__repr__() - Developer-Friendly StringΒΆ

Called in interactive mode and for debugging. Should be detailed and unambiguous:

def __repr__(self):
    """Return string for developers."""
    return (f"BankAccount(holder_name='{self._holder_name}', "
            f"account_type='{self._account_type}', "
            f"balance={self._balance})")

# Usage in Python REPL
account = BankAccount("Alice", "Savings", 1500)
account  # Calls __repr__()
# Output: BankAccount(holder_name='Alice', account_type='Savings', balance=1500)

7. Single Responsibility PrincipleΒΆ

Each method should do ONE thing clearly.

Good ExampleΒΆ

class BankAccount:
    def deposit(self, amount):
        """Do ONE thing: add money to account."""
        self._balance += amount

    def withdraw(self, amount):
        """Do ONE thing: remove money from account."""
        if amount > self._balance:
            raise ValueError("Insufficient funds")
        self._balance -= amount

    def calculate_interest(self):
        """Do ONE thing: calculate and apply interest."""
        interest = self._balance * (self._interest_rate / 100)
        self._balance += interest
        return interest

BenefitsΒΆ

  • Easy to test (each method has one behavior)
  • Easy to maintain (change one responsibility at a time)
  • Easy to reuse (clear purpose)
  • Easy to understand (no side effects)

8. Implementation Pattern: 7 StepsΒΆ

The Session 4 slides show a complete 7-step implementation pattern using BankAccount:

  1. Class Definition & __init__ - Initialize all instance variables
  2. @property Decorators - Provide read-only access
  3. @setter Decorators - Controlled write access with validation
  4. Regular Methods - Operations on the object
  5. Calculation Methods - Complex operations
  6. Status Methods - Report object state
  7. Magic Methods - __str__() and __repr__()

See the slides for complete, working code examples of each step.


9. Your Assessment: RobotBaseΒΆ

You will apply these OOP patterns to implement a RobotBase class for robotics control.

What You GetΒΆ

  • Specification document - What RobotBase must do
  • Unit tests - Automated feedback on correctness
  • Starter template - Method signatures to fill in
  • Competency checklist - Self-verification

What You Don't GetΒΆ

  • No complete code examples for RobotBase
  • No working implementation to copy
  • No step-by-step implementation guide

Why This MattersΒΆ

Seeing the BankAccount pattern teaches you OOP. Building RobotBase independently proves you understand it.


10. Key Concepts SummaryΒΆ

Concept What It Is Why It Matters
Class Blueprint for objects Defines structure and behavior
Object Instance of a class Each has independent state
self Reference to current object Python knows which object to modify
Private variables _name with underscore Signals "internal only"
@property Decorator for getter Clean read-only access
@setter Decorator for validation Protected write access
Magic methods __str__, __repr__ Called automatically by Python
Single Responsibility One method, one job Easier to test, maintain, understand

  • Session 4 Slides - Full BankAccount implementation (study this!)
  • Assessment Specification - RobotBase requirements
  • Unit Tests - What your code must pass
  • Competency Checklist - Self-verify your understanding

SummaryΒΆ

In Session 4 you will:

  1. Learn OOP patterns via BankAccount examples
  2. Understand why encapsulation, validation, and magic methods matter
  3. See a complete 7-step implementation pattern
  4. Apply these patterns to RobotBase in your assessment

The assessment tests whether you can apply patterns, not whether you can copy code. Study the BankAccount examples, understand the patterns, then implement RobotBase independently.


Ready? Check out the slides and let's code! πŸš€


Session 4 Assessment: RobotBase ClassΒΆ

OverviewΒΆ

Between Session 4 and Session 5, you will implement a RobotBase class from a UML diagram. This assessment tests whether you can apply the OOP patterns you learned with BankAccount to a different domain (robotics).

Key Point: You've seen the full BankAccount implementation. RobotBase is specification-only β€” you must understand patterns to implement it independently.

What You'll BuildΒΆ

A RobotBase class matching this UML design:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                   RobotBase                     β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ - _name : str                                   β”‚
β”‚ - _battery_level : float                        β”‚
β”‚ - _is_moving : bool                             β”‚
β”‚ - _sensor_readings : dict                       β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ + name : str              Β«read-only propertyΒ»  β”‚
β”‚ + battery_level : float   Β«read/write propertyΒ» β”‚
β”‚ + is_moving : bool        Β«read-only propertyΒ»  β”‚
β”‚ + sensor_readings : dict  Β«read-only propertyΒ»  β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ + move_forward(speed: int) : None               β”‚
β”‚ + stop() : None                                 β”‚
β”‚ + get_sensor_reading(name: str) : float         β”‚
β”‚ + set_sensor_reading(name: str, value: float)   β”‚
β”‚ + report_status() : str                         β”‚
β”‚ + __str__() : str                               β”‚
β”‚ + __repr__() : str                              β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

How to Approach ItΒΆ

  1. Study the UML Diagram β€” Understand the class structure above
  2. Study the BankAccount Examples β€” How patterns were applied to banking
  3. Connect the Patterns β€” See how BankAccount patterns apply to RobotBase
  4. Implement Independently β€” Write your own code, don't copy
  5. Submit β€” Save as solution.py and submit by [due date]

Key Patterns to ApplyΒΆ

From BankAccount, transfer these patterns to RobotBase:

Pattern BankAccount Example RobotBase Application
Private Variables self._balance self._battery_level, self._is_moving
@property Getter balance property (read-only) battery_level property (read-only)
@setter Validation balance.setter checks >= 0 battery_level.setter checks 0-100
Action Methods deposit(), withdraw() move_forward(), stop()
Sensor Access get_balance() method get_sensor_reading(), set_sensor_reading()
str User-friendly output Robot status display
repr Developer-friendly output Full state for debugging
Single Responsibility One method = one job Each method has one purpose

What You Can UseΒΆ

βœ… Study These: - Session 4 slides (BankAccount implementation) - BankAccount code examples in content - UML diagram above (class structure)

❌ Don't Do This: - Copy code from slides (it's BankAccount, not RobotBase) - Look for RobotBase code anywhere (there isn't any) - Copy from classmates - Use external libraries (only Python standard library)

GradingΒΆ

This is competency-based. Your implementation is assessed on:

  • βœ… Code follows OOP patterns correctly
  • βœ… Validation enforced (battery constraints, movement logic)
  • βœ… Magic methods implemented (__str__, __repr__)
  • βœ… Private variables and properties used correctly
  • βœ… Methods have single responsibility

Your code either demonstrates the competency or it needs revision.

Getting HelpΒΆ

If stuck: 1. Review the UML diagram β€” Does your class structure match? 2. Compare to BankAccount β€” How was this pattern implemented there? 3. Ask in office hours β€” But don't ask for code, ask about patterns


Assessment Timeline: - Session 4 (today): Learn patterns, review UML, start implementation - Between sessions: Complete implementation - Session 5: Submit solution, discuss patterns