OOP Concepts Overview

Module 3 • Session 21 • Fundamentals of Object-Oriented Software Design

oop-pillars duck-typing name-mangling composition solid-principles

Object-Oriented Programming (OOP) is a design paradigm that structures software around state (data attributes) and behaviors (methods). Python is a multi-paradigm language where everything—including numbers, strings, functions, and modules—is an instance of a class.

1. Procedural vs. Object-Oriented Programming

Procedural programming organizes code around sequential functions operating on decoupled data structures. OOP binds state and behavior into self-contained objects, enhancing modularity and maintainability.

Dimension Procedural Programming Object-Oriented Programming
Primary Unit Functions operating on external data structures. Objects encapsulating state and methods.
Data Access Global or mutable state passed across functions. Encapsulated access via controlled interfaces.
Extensibility Modifying existing procedures across modules. Extending classes through inheritance and composition.

2. The Four Core Pillars of OOP

A. Encapsulation & Name Mangling

Encapsulation restricts direct access to an object's internal state. Python uses naming conventions to signal visibility: a single underscore (_protected) signals internal use, while a double underscore (__private) triggers name mangling to transform the attribute name to _ClassName__attribute.

class BankAccount: def __init__(self, owner, balance): self.owner = owner self._account_type = "Checking" # Protected convention self.__balance = balance # Private (Name Mangled) def deposit(self, amount): if amount > 0: self.__balance += amount return True return False def get_balance(self): return self.__balance acc = BankAccount("Alice", 1000) acc.deposit(500) print("Balance via getter:", acc.get_balance()) # Direct private access fails with AttributeError: # print(acc.__balance) # Accessing via name-mangled key: print("Mangled access:", acc._BankAccount__balance)

B. Abstraction via Abstract Base Classes (ABC)

Abstraction hides implementation details behind unified contracts. Using Python's abc module, classes inheriting from ABC with @abstractmethod force subclasses to implement specific interfaces before instantiation.

from abc import ABC, abstractmethod class DatabaseConnector(ABC): @abstractmethod def connect(self): pass @abstractmethod def execute_query(self, query): pass class PostgresConnector(DatabaseConnector): def connect(self): return "Connected to PostgreSQL Database Engine." def execute_query(self, query): return f"Executing '{query}' on PostgreSQL." pg = PostgresConnector() print(pg.connect()) print(pg.execute_query("SELECT * FROM users"))

C. Inheritance

Inheritance lets child classes acquire attributes and methods from parent classes, promoting code reuse. Python uses Method Resolution Order (MRO) with C3 Linearization to resolve calls across complex inheritance chains.

class User: def __init__(self, username, email): self.username = username self.email = email def get_role(self): return "Standard User" class AdminUser(User): def get_role(self): return "System Administrator" admin = AdminUser("sys_admin", "admin@compillo.com") print(f"User: {admin.username} | Role: {admin.get_role()}")

D. Polymorphism & Duck Typing

Polymorphism lets different classes handle the same method interface differently. Python relies heavily on Duck Typing: "If it walks like a duck and quacks like a duck, it's a duck." Execution depends on method presence rather than class inheritance.

class JSONExporter: def export(self): return "{'status': 'success', 'code': 200}" class CSVExporter: def export(self): return "status,code\nsuccess,200" # Duck Typing in action: No shared inheritance required def render_export(exporter_instance): print("Export Output:\n" + exporter_instance.export()) render_export(JSONExporter()) render_export(CSVExporter())

3. Composition vs. Inheritance

Inheritance enforces an "Is-A" relationship, while composition represents a "Has-A" relationship. Design principles advocate for "Composition over Inheritance" to minimize brittle class hierarchies and create flexible system architectures.

class Engine: def start(self): return "V8 Engine firing up..." class GPS: def route(self): return "Calculating route to destination..." # Car "Has-A" Engine and "Has-A" GPS (Composition) class Car: def __init__(self): self.engine = Engine() self.gps = GPS() def drive(self): print(self.engine.start()) print(self.gps.route()) my_car = Car() my_car.drive()

4. High-Level SOLID Design Principles

  • Single Responsibility (SRP): A class should have one reason to change.
  • Open/Closed (OCP): Software entities should be open for extension, but closed for modification.
  • Liskov Substitution (LSP): Subtypes must be substitutable for their base types without altering correctness.
  • Interface Segregation (ISP): Clients should not be forced to depend on methods they do not use.
  • Dependency Inversion (DIP): Depend upon abstractions (ABCs), not concrete implementations.

🏋️ Try It Yourself: Advanced Challenges

Challenge 1: Encapsulated Wallet with Name Mangling

Construct a `CryptoWallet` class featuring private `__private_key` and `__balance` attributes. Expose explicit balance query methods while verifying name mangling in the attribute dictionary.

Challenge 2: Abstract Payment Processor

Create an Abstract Base Class `PaymentGateway` with abstract methods `authorize()` and `process()`. Implement concrete subclasses `StripeGateway` and `PayPalGateway`.

Challenge 3: Duck Typing Notification Pipeline

Create three distinct classes (`EmailSender`, `SMSSender`, `WebhookSender`) implementing a `send_payload(msg)` method without shared inheritance, and execute them via a single processing function.

Challenge 4: Modular System Composition

Design a `Computer` class composed of distinct `CPU`, `RAM`, and `Storage` class instances, triggering startup diagnostics sequentially.

⚡ Interactive Sandbox (Lesson 21: OOP Concepts)
Console Output:
Click "Run Code" above to execute OOP scripts...

📝 Knowledge Check Quiz

1. How does Python handle attributes prefixed with double underscores (e.g., `__data`) inside a class?
2. What defines "Duck Typing" in Python?
3. What is required for a class inheriting from an Abstract Base Class (ABC) to be instantiated?
Advertisement
Horizontal Ad Banner Slot (728x90 / Responsive)