Please use a larger screen

This presentation is designed for desktop or projector displays.

← Back to curriculum

PYTHON OOP — COURSE 12

Course Recap

Everything we've covered — in 9 quick stops

to navigate

Course Curriculum

Where we are in the journey

How This Lecture Works

Topic 1

Classes & Objects

Quick recap, then quizzes from the exam pool

What Does a Class Define?

Attributes

The data each object will hold

self.title = title
self.author = author
self.year = year

Methods

The behavior each object can perform

def display_info(self):
    print(f"{self.title}")
A class is a template. It doesn't represent a tangible item until we create an Object.

Creating Classes in Python

  • class keyword defines a new class
  • __init__ is the constructor
  • self refers to the current instance
  • Methods define object behavior
class Book:
    def __init__(self, title, author, year):
        self.title = title
        self.author = author
        self.year = year

    def display_info(self):
        print(f"{self.title} by {self.author} ({self.year})")

Creating Objects (Instantiation)

Syntax:

object_name = ClassName(parameters)

Example:

book1 = Book("Clean Code", "Robert Martin", 2008)
book2 = Book("Refactoring", "Martin Fowler", 1999)
Book book1 "Clean Code" book2 "Refactoring"

Key Takeaway: self

self = the specific instance

  • Always the first parameter in methods
  • Refers to the object that called the method
  • Python passes it automatically

When you write:

person1.introduce()

Python sees:

Person.introduce(person1)

Common Magic Methods

__len__

Called by len(obj)

Returns length/size of object

__add__

Called by obj1 + obj2

Defines addition behavior

__str__ / __repr__

Called by print(obj)

String representation

These methods let your classes work with Python's built-in operators and functions.

Quiz Time! (Classes & Objects — Class)

What is a class in programming?

A) A specific instance of an object

B) A template for creating objects

C) A type of function in a program

D) A variable that holds data

A class is a template (blueprint) for creating objects. Each object built from a class shares the same structure but has its own data.

Quiz Time! (Classes & Objects — Object)

Which statement best describes an object in OOP?

A) A function that performs a specific task

B) A prototype for classes

C) An instance of a class

D) A collection of classes

An object is an instance of a class. The class defines the structure; the object holds the actual data and can call the methods.

Quiz Time! (Classes & Objects — OOP Pillars)

Which of these is NOT one of the four pillars of OOP?

A) Encapsulation

B) Abstraction

C) Compilation

D) Polymorphism

The four pillars are Abstraction, Encapsulation, Inheritance, Polymorphism. Compilation is unrelated — it's a build step, not an OOP concept.

Topic 2

Encapsulation & Access Control

Quick recap, then quizzes from the exam pool

Four Pillars of OOP

The core characteristics of object-oriented programming

🛡

Abstraction

Hide complexity, show essentials

🔒

Encapsulation

Bundle data & restrict access

🔁

Polymorphism

One interface, many forms

📂

Inheritance

Reuse & extend existing code

Encapsulation: Information Hiding

  • Encapsulation = bundling data + methods that operate on that data
  • Information hiding = restricting direct access to internal state
  • External code interacts only through public methods
  • Internal data is protected from accidental modification
ENCAPSULATED OBJECT public methods() _protected data __private

Access Control in Python

Python uses naming conventions, not strict enforcement

Prefix Level Meaning
name Public Accessible from anywhere
_name Protected Convention: internal use only
__name Private Name mangling applied

Access Control Comparison

Feature Public Protected _ Private __
Access from outside ✓ (discouraged) ✗ (mangled)
Access from subclass ✗ (mangled)
Enforced by Python N/A No (convention) Partially
Use case API / interface Internal logic Avoid name clashes

The @property Decorator

Same thing, but Pythonic

class MyClass:
    def __init__(self, value):
        self._value = value

    @property
    def value(self):
        return self._value

    @value.setter
    def value(self, new_value):
        if new_value >= 0:
            self._value = new_value

obj = MyClass(10)
print(obj.value)  # 10 (uses getter)
obj.value = 20    # uses setter
Looks like attribute access, but runs validation logic behind the scenes

@property: Circle Example

import math

class Circle:
    def __init__(self, radius):
        self._radius = radius

    @property
    def radius(self):
        return self._radius

    @radius.setter
    def radius(self, value):
        if value <= 0:
            raise ValueError("Radius must be positive")
        self._radius = value

    @property
    def area(self):
        return math.pi * self._radius ** 2

c = Circle(5)
print(c.area)  # 78.54 (computed property)

Quiz Time! (Encapsulation & Access Control — Definition)

What does encapsulation refer to in object-oriented programming?

A) The process of inheriting properties from a base class

B) The practice of keeping data and the methods that modify it together within a class

C) Dividing a program into multiple distinct classes

D) The interaction between different objects

Encapsulation bundles data + the methods that operate on that data inside a single class — and hides internals from outside code.

Quiz Time! (Encapsulation & Access Control — Private)

In Python, which prefix is used to declare a private variable?

A) private

B) protect

C) __ (double underscore)

D) private:

Python uses naming conventions: a double underscore prefix triggers name-mangling (private-ish). Single underscore _x is just a convention for «protected».

Quiz Time! (Encapsulation & Access Control — Access)

How are private variables of a Python class typically accessed from outside?

A) Directly by their name

B) They cannot be accessed at all

C) Through reflection library functions

D) Through public methods (getters / setters) defined in the class

The Pythonic way: expose public methods (or @property) that act as getters/setters. This keeps the internal data hidden and gives you a place to validate writes.

Topic 3

Inheritance

Quick recap, then quizzes from the exam pool

Inheritance

Real-world hierarchies map to code

  • A Vehicle can be a wheeled vehicle or a boat
  • A Car and a Bicycle are both wheeled vehicles
  • A Truck and Sports Car are both cars
  • Each level inherits from the one above and adds its own specifics
Vehicle 🚗 Wheeled Vehicle ⚙️ Boat Car 🚘 Bicycle 🚲 Truck 🚚 Sports Car 🏎️

Terminology

Base Class (Superclass)

The class from which properties and methods are inherited. Also known as a "parent" class.

Derived Class (Subclass)

The class that inherits from another class. Can add new properties or modify existing ones. Also known as a "child" class.

Inheritance

Allows creation of a new class that is a modified version of an existing class.

Animal 🐾 Parent / Superclass Dog 🐶 Child / Subclass Cat 🐱 Child / Subclass

Terminology in Code

class Animal: # Parent class
    def eat(self):
        print("This animal is eating")

class Dog(Animal): # Child class
    def bark(self):
        print("The dog barks")

class Cat(Animal): # Child class
    def meow(self):
        print("The cat meows")

rex = Dog()
rex.eat()   # This animal is eating
rex.bark()  # The dog barks
Animal eat() Base class (parent) Inheritance Dog bark() + eat() Cat meow() + eat() Derived classes (children)
class Dog(Animal) — the parentheses declare the inheritance relationship

__init__ in Inheritance

How constructors chain through the hierarchy

The Problem

Child's __init__ overrides parent's. Parent attributes are lost!

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

class Dog(Animal):
    def __init__(self, name, breed):
        self.breed = breed  # name is LOST!

rex = Dog("Rex", "Labrador")
print(rex.breed)  # Labrador
print(rex.name)   # AttributeError!

The Solution

Call super().__init__() to chain constructors

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

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

rex = Dog("Rex", "Labrador")
print(rex.name)   # Rex ✔
print(rex.breed)  # Labrador ✔
Rule: Always call super().__init__() when overriding the constructor

Understanding Method Overriding

A subclass provides a specific implementation for a method already defined in its superclass

Customization

Subclasses tailor or modify inherited behavior without altering the superclass code

Extensibility

Subclasses can extend behavior by adding steps and calling super()

Polymorphism

A single interface can represent different underlying forms

The super() Function

Access superclass methods — ensures proper initialization

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

class Dog(Animal):
    def __init__(self, name, breed):
        super().__init__(name)
        self.breed = breed
super() can call any method from the parent class, not just the constructor.
  • Access superclass methods that have been overridden
  • Ensures the superclass is properly initialized
  • Allows subclasses to extend rather than replace behavior
  • Respects the class hierarchy and MRO

Quiz Time! (Inheritance — super())

How would you call the constructor of the parent class from a child class in Python?

A) Using the parent keyword

B) Using the super() function

C) Using the self keyword

D) Using both self and super()

super().__init__(...) chains to the parent constructor. Without it, parent attributes never get initialised — you'll hit AttributeError later.

Quiz Time! (Inheritance — Terminology — multi-select)

Which terms are valid synonyms for «parent class»? (select all that apply)

A) Base class

B) Subclass

C) Derived class

D) Superclass

Parent / Base / Super — same thing. Child / Sub / Derived — same thing. Don't mix them up on the exam.

Quiz Time! (Inheritance — Diamond)

What problem can occur when using multiple inheritance?

A) Diamond problem

B) Circle inheritance

C) Random resolution

D) Memory leak

The Diamond problem — a class inherits from two classes that share a common ancestor, creating ambiguity about which method to call. Python resolves this with the MRO.

Topic 4

Polymorphism

Quick recap, then quizzes from the exam pool

Understanding Polymorphism

A fundamental OOP concept where objects of different classes can be treated as objects of a common super class

  • poly = many, morph = form
  • The ability of one interface to represent multiple forms
  • Same method name, different behaviors depending on the object
  • Enables writing flexible and extensible code
speak() Dog "Woof!" Cat "Meow!" Duck "Quack!" Same method call Different results

Polymorphism Code

class Animal:
    def speak(self):
        pass

class Dog(Animal):
    def speak(self):
        return "Woof!"

class Cat(Animal):
    def speak(self):
        return "Meow!"

animals = [Dog(), Cat(), Dog(), Cat()]

for animal in animals:
    print(animal.speak())
Output: Woof! Meow! Woof! Meow! — same method call, different behavior!

Static vs Dynamic Polymorphism

Compile-time (Static)

  • Resolved at compile time
  • Achieved through method overloading
  • Determined by method signature
  • Common in Java, C++
Overloading

Runtime (Dynamic)

  • Resolved at runtime
  • Achieved through method overriding
  • Determined by object's actual type
  • Primary form in Python
Overriding

Duck Typing Principle

  • Focuses on what an object can do, not what it is
  • If the object has the method your code expects, use it — regardless of type
  • No need for inheritance or shared base class
  • Pythonic way to achieve polymorphism
class Duck:
    def quack(self):
        print("Quack!")

class Person:
    def quack(self):
        print("I can quack too!")

def make_it_quack(thing):
    thing.quack()

make_it_quack(Duck())
make_it_quack(Person())
make_it_quack() doesn't care about the type — only that the object has a quack() method

Abstract Classes Intro

  • A blueprint for other classes
  • Cannot be instantiated directly
  • Contains abstract methods with no implementation
  • Subclasses must implement all abstract methods
  • Enforces a contract — "you must have these methods"
Abstract Class method() = ??? Concrete A method() ✓ Concrete B method() ✓ ✗ AbstractClass() Cannot instantiate!

ABC + @abstractmethod

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
Shape() raises TypeError — you must use a concrete subclass like Circle(5)

Quiz Time! (Polymorphism — Definition)

What is polymorphism in Python?

A) A method always does the same thing regardless of the object

B) Accessing properties of one class from another class

C) A method does different things based on the object that it is acting upon

D) Inheriting properties and methods from another class

Polymorphism — one interface, many forms. Same method call (animal.speak()), different result depending on the object's actual class.

Quiz Time! (Polymorphism — Abstract)

Which decorator is used to define an abstract method in Python?

A) @abstract

B) @abstractdecorator

C) @abstractmethod

D) @override

Use @abstractmethod from the abc module. The class itself must inherit from ABC — then it can't be instantiated until all abstract methods are implemented.

Quiz Time! (Polymorphism — Overriding)

What is method overriding?

A) Deleting a method from a parent class

B) Modifying an existing method in the parent class

C) Creating a new method in a child class with the same name but different parameters

D) Creating a new method in a child class with the same name and parameters

Overriding — same signature, new implementation in the child class. (Different parameters = overloading, which Python doesn't natively support.)

Topic 5

Composition & Aggregation

Quick recap, then quizzes from the exam pool

Object Relationships

Two fundamental types

is-a Inheritance

A subclass is a type of its parent

  • 🐶 Dog is a Animal
  • 🚗 Car is a Vehicle
  • 👤 Manager is a Employee

has-a Association

An object has a reference to another object

  • 🚗 Car has a Engine
  • 📚 Library has Books
  • ⚽ Team has Players
Today's focus: the has-a side — composition and aggregation

What Is Composition?

  • A strong has-a relationship
  • The owner creates and manages the parts
  • Parts are exclusive — they belong to only one owner
  • When the owner is destroyed, all parts are destroyed too
  • Lifecycle is tightly coupled
Think: a house and its rooms. Demolish the house → rooms are gone
🏠 House 🍳 Kitchen 🛌 Bedroom 🚽 Bathroom Owner destroyed → all parts destroyed

Composition in Python

class Engine:
    def __init__(self, horsepower):
        self.horsepower = horsepower

    def start(self):
        return "Engine running"


class Car:
    def __init__(self, model):
        self.model = model
        self.engine = Engine(200)

    def drive(self):
        return self.engine.start()
  • Car creates the Engine inside __init__
  • Engine is not passed in — it's created internally
  • If the Car object is deleted, the Engine goes with it
  • No other object references this engine
# Usage
my_car = Car("Toyota")
print(my_car.drive())
# Engine running

What Is Aggregation?

  • A weak has-a relationship
  • Objects are passed in from outside, not created internally
  • Parts can be shared across multiple owners
  • Parts survive after the owner is destroyed
  • Lifecycle is independent
Think: a team and its players. Dissolve the team → players still exist
Team 🧍 Player 1 🧍 Player 2 🧍 Player 3 Owner destroyed → parts survive

Composition vs Aggregation

Composition Aggregation
Relationship Strong "has-a" Weak "has-a"
Ownership Exclusive Shared
Lifecycle Parts die with owner Parts live independently
Creation Created inside owner Passed in from outside
Coupling Tight Loose
Example House & Rooms Team & Players

Composition over Inheritance

A principle suggesting classes should achieve polymorphism through composition rather than inheritance

  • Instead of inheriting properties from a parent class...
  • ...a class should be composed of other classes that provide desired behavior
  • More flexible — can change behavior at runtime
  • Avoids fragile base class problem
Inheritance: Manager is-a Employee
Composition: Employee has-a Role

Quiz Time! (Composition & Aggregation — has-a)

Which relationship best describes composition in OOP?

A) is-a

B) can-do

C) uses-a

D) has-a

Composition is a has-a relationship (Car has-a Engine). Inheritance is is-a (Dog is-a Animal).

Quiz Time! (Composition & Aggregation — Aggregation)

Which is a key difference between aggregation and composition?

A) In aggregation, the part can exist independently of the whole

B) In composition, objects are linked using pointers

C) Aggregation implies stronger ownership than composition

D) There is no real difference

Composition = parts die with the owner (House & Rooms). Aggregation = parts survive independently (Team & Players).

Quiz Time! (Composition & Aggregation — Example)

A Car class creates and owns an Engine object inside its __init__. This is an example of...

A) Aggregation

B) Composition

C) Inheritance

D) Dependency injection

Creating the part inside the owner and tying its lifecycle to the owner = composition. If Engine were passed in from outside, that would be aggregation / DI.

Topic 6

Exception Handling

Quick recap, then quizzes from the exam pool

What is Exception Handling?

Exception handling is a programming mechanism that enables developers to manage errors and anomalies that arise during the execution of a program.

In essence, it allows a program to continue running or gracefully terminate when it encounters unexpected situations, rather than crashing abruptly.

Program Error! Handle & recover

Built-in Python Exceptions

  1. AssertionError: assert statement fails
  2. EOFError: input() meets end-of-file
  3. AttributeError: attribute assignment fails
  4. ImportError: importing a module fails
  5. IndexError: sequence index out of range
  6. KeyboardInterrupt: Ctrl+C pressed
  7. RuntimeError: no category fits
  8. NameError: variable not found
  1. MemoryError: out of memory
  2. ValueError: right type, wrong value
  3. ZeroDivisionError: division by zero
  4. SyntaxError: invalid Python syntax
  5. IndentationError: wrong indentation
  6. SystemError: internal interpreter error
  7. TypeError: operation on wrong type

try / except: Flow

try: result = 10 / x data = load_file() process(data) Exception raised! except: log_error(e) show_message() use_fallback() Instead of crashing, your code moves on to alternative solutions.

try / except / finally

try else except finally Always runs, no matter what.

The finally keyword in the try-except block is always executed, irrespective of whether there is an exception or not.

It is quite useful in cleaning up resources and closing the object, especially closing the files.

try:
    # Code that might raise an exception
except ExceptionType:
    # Code that runs if an exception occurs
finally:
    # Cleanup code that runs no matter what

Creating custom exceptions

Custom exceptions are defined by subclassing Python's built-in Exception class.

The simplest form:

class MyCustomError(Exception):
    pass

With a constructor:

class ValidationError(Exception):
    def __init__(self, message, value):
        self.message = message
        self.value = value
        super().__init__(message)

Logging Levels

2023-04-01 09:15:30 - root - DEBUG    - This is a debug message
2023-04-02 10:20:45 - root - INFO     - This is an info message
2023-04-03 11:25:50 - root - WARNING  - This is a warning message
2023-04-04 12:30:55 - root - ERROR    - This is an error message
2023-04-05 13:35:00 - root - CRITICAL - This is a critical message

Quiz Time! (Exception Handling — except)

Which keyword is used immediately after try to catch exceptions in Python?

A) catch

B) except

C) finally

D) error

except. (Java/C# use catch, but Python uses except.) You can specify the exception type: except ValueError:

Quiz Time! (Exception Handling — finally)

Which block always runs, whether or not an exception occurred?

A) except

B) finally

C) else

D) before

finally always runs — ideal for cleanup like closing files or releasing locks. else only runs if no exception was raised.

Quiz Time! (Exception Handling — raise)

Which keyword is used to manually raise an exception in Python?

A) throw

B) raise

C) error

D) exception

raise — e.g. raise ValueError("negative balance"). Java/C# use throw; Python uses raise.

Quiz Time! (Exception Handling — Custom)

When writing a custom exception in Python, which class should you derive from?

A) Error

B) Exception

C) BaseError

D) CustomError

Inherit from Exception (or a more specific subclass like ValueError). Avoid inheriting from BaseException directly — that catches things like KeyboardInterrupt.

Topic 7

SOLID Principles

Quick recap, then quizzes from the exam pool

SOLID

S Single Responsibility O Open-Closed Principle L Liskov Substitution I Interface Segregation D Dependency Inversion

What SRP IS:

We want to:

Cohesion

How well the internal elements of a module work together to fulfill a single, well-defined purpose.

Coupling

The degree of direct knowledge or reliance one module has on another. Lower is better.

OCP Visualized

❌ Usual way: modify existing Blue = changed — changes scattered everywhere ✅ OCP way: extend only 🧩 Green = new — just add a new piece

Liskov Substitution Principle (LSP)

Substitutable = capable of being exchanged.

Users of a base class should continue to function properly if a derivative of that base class is passed to it.

💡 If you have a function that works with Animal, it should also work with Dog (which is a type of Animal) without needing any special adjustments.

Named after Barbara Liskov, who introduced it in 1987.

Interface Segregation Principle

Ensure that your classes only implement the methods they need. If a class is forced to implement methods it doesn't use, consider breaking your interface (or ABC) into smaller, more specific ones.

💡 Think of it like USB cables: individual cables for each device (✅) vs one cable with every connector (❌).

DIP Visualized

❌ Without DIP High-Level Module Low-Level Module ✅ With DIP High-Level Module Abstraction Layer Low-Level Module

Quiz Time! (SOLID Principles — SRP)

Which SOLID principle states that a class should have only one reason to change?

A) Single Responsibility Principle

B) Open/Closed Principle

C) Liskov Substitution Principle

D) Dependency Inversion Principle

SRP — one class, one responsibility. If two unrelated reasons could force you to edit the same class, split it.

Quiz Time! (SOLID Principles — OCP)

Which SOLID principle states that a class should be open for extension and closed for modification?

A) Single Responsibility Principle

B) Open/Closed Principle

C) Liskov Substitution Principle

D) Dependency Inversion Principle

OCP — you should be able to add new behavior (extend) without rewriting existing code (modify). Achieved via inheritance, composition, or strategy patterns.

Quiz Time! (SOLID Principles — LSP)

Which SOLID principle states that objects of a superclass should be replaceable with objects of its subclasses without breaking the application?

A) Single Responsibility Principle

B) Open/Closed Principle

C) Liskov Substitution Principle

D) Dependency Inversion Principle

LSP — named after Barbara Liskov. If code expects an Animal, passing a Dog should still work correctly. Penguins shouldn't break a Bird.fly() contract.

Quiz Time! (SOLID Principles — DIP — multi-select)

Which statements about the Dependency Inversion Principle (DIP) are correct? (select all that apply)

A) High-level modules should not depend on low-level modules

B) Low-level modules should not depend on high-level modules

C) Both should depend on abstractions, not on concrete classes

D) Abstractions should depend on details (concrete classes)

DIP has two halves: high-level code shouldn't depend on low-level code, and both should depend on abstractions. Dependency injection is one common technique for following DIP.

Topic 8

Design Patterns

Quick recap, then quizzes from the exam pool

What is a Design Pattern?

A standardized solution to a commonly occurring problem in software design.

They are templates, not ready-made code. You adapt the pattern to fit your specific problem.

💡 Think of patterns as blueprints — they show the approach, but the implementation is yours.

Three Categories

🏭

Creational

How objects are created

  • Singleton
  • Factory Method
  • Abstract Factory
  • Builder
  • Prototype

🧩

Structural

How objects are composed

  • Adapter
  • Decorator
  • Proxy
  • Composite
  • Facade

💬

Behavioral

How objects communicate

  • Observer
  • Strategy
  • Command
  • Iterator
  • State

Singleton in Python

class DatabaseSingleton:
    _instance = None

    def __new__(cls):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
            cls._instance.connection = "Connected to DB"
        return cls._instance

    def query(self, sql):
        return f"Executing: {sql}"

✅ How it works:

  • _instance stores the single object (starts as None)
  • __new__ checks: if no instance exists → create one; otherwise → return existing
  • Every call to DatabaseSingleton() returns the same object

Quiz Time! (Design Patterns — Factory)

Which describes the role of the Factory Method pattern?

A) Defines the skeleton of an algorithm

B) Creates objects without specifying the exact class

C) Allows classes to work together that couldn't otherwise

D) Provides global access to a single instance

Factory Method — centralizes object creation so callers ask «what» they need, and the factory decides «which class». («Global access» describes Singleton; «classes that couldn't work together» describes Adapter.)

Quiz Time! (Design Patterns — Builder)

Which design pattern is used to construct complex objects step by step?

A) Decorator

B) Singleton

C) Builder

D) Proxy

Builder — useful when an object has many optional parameters or a multi-step construction process. Call .withX().withY().build() instead of a 12-arg constructor.

Quiz Time! (Design Patterns — Singleton)

What is the purpose of the __new__ method in Python?

A) Initializing the newly created object

B) Creating and returning a new instance of a class

C) Calling after __init__ to set up the object

D) Cleaning up the object after it is no longer needed

__new__ creates and returns the instance; __init__ then initialises it. Overriding __new__ is how the Singleton pattern is typically implemented in Python.

Strategy — Real-world Analogy

Imagine you have to get to the airport. Different strategies:

  • 🚌 Bus — cheapest, but slowest
  • 🚕 Taxi — fast and comfortable, but expensive
  • 🚲 Bicycle — free and healthy, but weather-dependent
Same goal (reach the airport), different strategies. You pick one depending on budget, time, or weather.
Transportation strategies: bus, cab, and bicycle to reach the airport

Decorator — Real-world Analogy

Wearing layers of clothing:

  • 👕 You start with a t-shirt (base object)
  • 🧣 Add a sweater when it's cold (decorator 1)
  • 🧥 Put on a jacket for rain (decorator 2)
  • 🌂 Add a raincoat for heavy rain (decorator 3)
Each layer wraps the previous one. You can add or remove layers without changing the t-shirt itself.
Decorator analogy: layering clothes — t-shirt, sweater, jacket, raincoat

Factory: Solution

  • 🔨 Centralize all creation logic in one place
  • 🟢 Clients ask what they need — the factory picks which class
  • 🔁 Adding a new product = one edit to the factory, zero changes to callers
Most useful when many inputs determine which class to instantiate — all that complexity lives in one place.

▶ The diagram on the right shows Factory Method (GoF) — a polymorphic variant with subclass overrides. In Python, the Simple Factory below (a single create() with if/else) is what you'll use 95% of the time.

Factory Method solution diagram

In practice — many params → one of many classes:

class TransportFactory:
    @staticmethod
    def create(destination, weight, urgent):
        if destination == "overseas":
            return Ship(weight)
        if urgent and weight < 10:
            return Drone(weight)
        if weight > 1000:
            return Train(weight)
        return Truck(weight)

t = TransportFactory.create("overseas", 50, False)

Facade: Solution

Observer — Real-world Analogy

Think of a YouTube channel:

  • 🎥 The channel (subject) publishes new videos
  • 👤 Subscribers (observers) get notified automatically
  • ❌ The channel doesn't care who subscribes or how many
  • ✅ Subscribers can join or leave freely — no coupling to the channel's internals
One publisher, many subscribers. Loose coupling.
YouTube subscribe button — the publisher-subscriber analogy for the Observer pattern

When to Use Which Pattern?

Pattern Use when…
Strategy You have several algorithms doing the same thing and want to pick at runtime
Decorator You need to add behavior to an object without subclassing
Factory Object creation logic shouldn't live in the caller
Facade Client code is drowning in subsystem complexity
Iterator You want to traverse a collection without exposing internals
Observer Multiple objects must react to a state change
Not sure? Write the simple version first. Refactor to a pattern when the real need appears.

Quiz Time! (Design Patterns — Decorator)

Which design pattern allows behavior to be added to an individual object, dynamically?

A) Adapter

B) Observer

C) Decorator

D) Factory

Decorator — wraps an object to add behavior at runtime, without subclassing. Think of layering clothes: t-shirt → sweater → jacket → raincoat.

Quiz Time! (Design Patterns — Structural)

Which patterns organize classes and objects to form larger structures and help keep parts of the system loosely coupled?

A) Creational

B) Structural

C) Behavioral

D) Concurrency

Three GoF families: Creational (how objects are made), Structural (how they're composed — Adapter, Decorator, Facade, Proxy), Behavioral (how they communicate).

Quiz Time! (Design Patterns — Categorize — multi-select)

Which of these are creational design patterns? (select all that apply)

A) Factory

B) Builder

C) Adapter

D) Singleton

Creational patterns deal with object creation: Singleton, Factory, Abstract Factory, Builder, Prototype. Adapter is structural — it adapts one interface to another.

Topic 9

Unit Testing & TDD

Quick recap, then quizzes from the exam pool

🧩 What is a "unit"?

If you're testing more than one class, it's no longer a unit test — that's integration.

📋 The AAA Pattern

Arrange — Act — Assert. Three lines, three responsibilities.

def test_rectangle_area():
    rect = Rectangle(10, 20)         # Arrange
    area = rect.area()               # Act
    assert area == 200               # Assert
Read top-to-bottom. Arrange the inputs, act on the subject, assert the outcome.

Same Test, Two Styles

unittest:

import unittest
from rectangle import Rectangle

class TestRectangle(unittest.TestCase):
    def test_area(self):
        rect = Rectangle(10, 20)
        self.assertEqual(rect.area(), 200)

pytest:

from rectangle import Rectangle

def test_area():
    rect = Rectangle(10, 20)
    assert rect.area() == 200

✅ Same behavior, half the code.

F.I.R.S.T Principles

Five qualities every good test should have.

What is TDD?

TDD is a development practice where you write tests before the code they test.

  • 🎯 Tests define the design
  • 📝 Production code exists only to make tests pass
  • 🔁 Continuous feedback loop, measured in minutes
It flips the order — tests first, code second.

The TDD Cycle

RED failing test GREEN make it pass REFACTOR clean up

Three steps. Endless cycles.

Quiz Time! (Unit Testing & TDD — TDD)

What are the main phases of test-driven development (TDD)?

A) Plan, develop, test

B) Red, green, refactor

C) Design, implement, test

D) Red, green, review

Red — write a failing test. Green — make it pass with minimal code. Refactor — clean up while keeping tests green. Repeat.

Quiz Time! (Unit Testing & TDD — Purpose)

What is the primary purpose of unit testing?

A) To test the entire system as a whole

B) To test individual units or components of the software

C) To test the performance of the software

D) To evaluate the usability of the software

Unit tests check one small piece (usually a method or class) in isolation. Whole-system tests are integration / end-to-end tests — different layer of the pyramid.

Quiz Time! (Unit Testing & TDD — Frameworks — multi-select)

Which frameworks are commonly used for unit testing in Python? (select all that apply)

A) pythontest

B) pytest

C) unittest

D) pythontesting

unittest ships with the standard library. pytest is the community favorite — less boilerplate, plain assert, great fixtures.

Quiz Time! (Unit Testing & TDD — Mocking)

Which statement about mocking in software testing is correct?

A) Mocking complicates testing as it requires extra mock objects

B) Mocking uses real objects in tests to ensure accuracy

C) Mocking creates full copies of objects for testing

D) Mocking simplifies testing by replacing complex dependencies with mock objects

Mocks replace slow or external things (databases, HTTP, time) with controllable fakes. The test becomes fast, deterministic, and focused on the unit you actually want to test.

Key Takeaways — The Whole Course

🎯 Exam Tips

📝 Exam Rules

💻 Where

On class computers in the lab.

No personal laptops, phones, or notes.

⏲ Time

35 minutes

Read every question twice — don't get stuck on one.

❓ Questions

20 questions

Multiple choice — some single-answer, some multi-select.

📈 Pace

~1 min 45 s per question

Skip the hard ones first, come back at the end.

📅 Exam Schedule

Consultation for all groups: 2026-05-15, 08:30, P2 153 · Exam venue: P2 203

Date Time Group
Tue 2026-06-0208:30Ef-25/1
10:20EEf-25/2
12:10EEf-25/1
Tue 2026-06-0908:30Ef-25/2
10:20EIf-25
11:10EDIf-25/1
12:10EAf-25
14:30EKf-25
Wed 2026-06-1010:20EDIf-25/2
12:10EIRf-25

Subject: Objektinis programavimas (su kursiniu darbu) · Code: FMSAB23201 · Type: E

Thank You!

Good luck on the exam — you've got this.

Questions? Ask now — this is your last chance before the exam.