Advanced Object Oriented Design and Programming

Duration: July 2025 (12 Weeks) | Tutor: Anupam Mazumdar

Learning Objectives

  • Advanced OOP Concepts: Deepen your understanding of OOP principles, design patterns, and software architecture.
  • Practical Application: Apply your knowledge through coding exercises, case studies, and a capstone project.
  • Real-World Relevance: Learn how to design software that addresses societal challenges, including sustainability and inclusivity.
  • Collaborative Learning: Engage with peers and instructors through live seminars and online forums.
  • Career Preparation: Gain skills that are highly relevant to careers in cybersecurity, data science, AI, and more.

Unit 1: Introduction and Recap of Object-Oriented Programming (OOP)


This unit re-established my foundational understanding of the four pillars of Object-Oriented Programming including Inheritance, Polymorphism, Encapsulation, and Abstraction, while reinforcing their application in designing secure, scalable, and maintainable software systems.

The practical exercises were implemented in Python 3 and focused on applying these principles through class hierarchies, abstract base classes, method overriding, and lifecycle management.

By completing this unit, I was able to:

  • Revisit and apply the four key OOP concepts using real-world examples.
  • Understand how classes, objects, constructors, destructors, and access control shape object behavior.
  • Recognize the role of OOP in large-scale, secure software design, where encapsulation and abstraction are vital for preventing data leaks and enforcing controlled access.
  • Implement practical Python programs demonstrating reusable and extensible design.

The below GitHub Repository shows the Programming Excercises completed during Unit 1

https://github.com/nb25877/Python-OOP/tree/main/Unit1



Unit 2: SOLID Principles of Object-Oriented Design

In this unit, I deepened my understanding of SOLID principles: five key guidelines for writing clean, maintainable, and extensible object-oriented software.

Through a hands-on Python case study of an online shopping system, I refactored an initially monolithic design into modular, testable components that align with each SOLID principle:

  1. Single Responsibility Principle (SRP): Each class now has a focused purpose, for example, Order manages cart logic, while PaymentProcessor handles payment operations independently.
  2. Open/Closed Principle (OCP): The system can be extended with new payment or discount methods without modifying existing code, improving scalability and reducing regression risk.
  3. Liskov Substitution Principle (LSP): All payment types (e.g., Credit, PayPal, Crypto) can substitute one another without affecting overall behavior, ensuring predictable system operation.
  4. Interface Segregation Principle (ISP): Interfaces were refined to include only relevant methods, keeping classes lightweight and avoiding unnecessary dependencies.
  5. Dependency Inversion Principle (DIP): High-level modules now depend on abstractions rather than concrete implementations, promoting loose coupling and flexible architecture.

This exercise demonstrated how SOLID principles not only improve code readability and maintainability but also enhance security and reliability which are essential attributes in large-scale software systems.

Below is the link for the Code prepared in Unit 2 Case Study

https://github.com/nb25877/Python-OOP/tree/main/Unit2




Unit 3 Summary: Creational Patterns in Practice

In Unit 3, I explored foundational creational design patterns and how they enhance object-creation logic in software. This unit built upon the OOP and SOLID principles from earlier weeks by focusing on the how of constructing objects — not just what they do.

I studied five central patterns:

  • Singleton: guarantees a class has exactly one instance and offers a global access point
  • Factory Method: defines a creator interface but lets subclasses determine which concrete object to instantiate
  • Builder: separates the construction of a complex object from its representation
  • Prototype: enables cloning objects without depending on their classes
  • Abstract Factory: produces families of related objects through consistent interfaces

My coded exercise concentrated on the Factory Method pattern. In my implementation (in the GitHub repo), I defined an abstract Creator class with a create() method and then concrete subclasses that return different product types. This design allowed the client code to remain agnostic of concrete classes, enhancing flexibility and reducing coupling.

Unit 3 deepened my understanding of how design patterns serve as blueprints for reusability and clarity in architecture. By solving object-creation challenges with patterns, I feel better equipped to create scalable, maintainable systems. The experience also reinforced the connection between SOLID principles (especially the Dependency Inversion Principle and Open/Closed Principle) and design pattern choices.

View Project Repository: Unit 3 – Design Patterns I (Creational Patterns)




Unit 4 Summary: Structural Patterns in Software Design

In Unit 4, I examined structural design patterns and ways to organise classes and objects into cohesive, adaptable systems. This unit expanded on the creational patterns explored earlier, moving the emphasis from object creation to composition and interaction among components.

I studied seven key structural patterns:

  • Adapter: enables collaboration between incompatible interfaces
  • Bridge: separates abstraction from implementation to support independent evolution
  • Composite: models part-whole hierarchies using recursive composition
  • Decorator: attaches new behaviour to objects dynamically without altering existing code
  • Facade: simplifies access to complex subsystems through a unified interface
  • Proxy: controls access to another object by acting as an intermediary
  • Flyweight: reduces memory consumption by sharing intrinsic data among similar objects

My exercise implemented the Decorator pattern through a coffee-shop application. I created a Coffee interface, a SimpleCoffee class, and decorators such as Milk, Sugar, and Whip. Each decorator introduced cost and description details, showing how new functionality can be layered without modifying the base component. This practical task demonstrated that structural patterns improve code modularity, scalability, and maintenance.

By completing this unit, I strengthened my understanding of design abstraction and code organisation. The experience illustrated that composition promotes reusability and flexibility — principles essential for secure and extensible architectures. Structural patterns also offered perspective on the way complex enterprise systems maintain clarity while integrating multiple subsystems.

View Project Repository: Unit 4 – Design Patterns II (Structural Patterns)


Unit 5: Design Patterns III – Behavioural Patterns

In Unit 5, I learned how behavioural design patterns shape the way objects interact. The unit helped me see how good communication between objects keeps a system simple, predictable, and easy to maintain.

I explored six key patterns.

  • Strategy helps switch between algorithms at runtime.
  • Observer keeps dependents updated when a subject changes.
  • Chain of Responsibility passes a request through handlers until one processes it.
  • Template Method defines an algorithm’s outline and lets subclasses fill in the details.
  • Command wraps a request as an object to enable queuing and undo.
  • State changes behaviour when an internal state changes.

For my exercise, I applied the Strategy pattern to refactor a payment processor. I built a PaymentStrategy interface and separate classes for credit card, PayPal, and bank transfer. The processor now delegates to the chosen strategy instead of using multiple conditionals. This made the code shorter, easier to read, and simpler to extend when adding a new payment method.

This activity reinforced the link between design patterns and the SOLID principles. The Open/Closed principle is visible when adding new strategies without touching old code. The Single Responsibility principle appears in each class handling one clear task.

The case study on the Observer pattern in cybersecurity systems showed how alerts trigger immediate responses when a monitored state changes. Together, these lessons improved my confidence in designing maintainable and adaptable systems.

View Project Repository: Unit 5 – Design Patterns III (Behavioural Patterns)


Unit 6: Concurrency and Parallelism in Object-Oriented Design

In Unit 6, I studied concurrency and parallelism and the ways they improve performance in modern applications. The unit explained threads, processes, and synchronisation, showing how they combine to maintain consistency in shared data. I applied these concepts by developing a thread-safe banking system in Python.

My program used the threading module and locks to protect account operations. Each account stored its balance using the Decimal type to prevent rounding errors. The BankAccount class managed deposits, withdrawals, and transfers safely. The TransactionSimulator created several threads to run random transactions on shared accounts. Unit tests in Tests.py verified that no race conditions or deadlocks occurred during concurrent runs.

This exercise showed me that object-oriented principles strengthen concurrency control. Encapsulation kept critical data private, and clear method boundaries made locking easier to apply. This reduced the chance of deadlocks and kept the code clear.

Feedback noted strengths such as realistic validation, IBAN-style identifiers, and complete testing. It also recommended adding detailed logs for auditing and including heavier stress tests. These suggestions will guide my future improvements.

Completing this unit increased my confidence in writing thread-safe programs. I now understand how to control concurrency through deliberate design and testing choices.

View Project Repository: Unit 6 – Concurrency and Parallelism in Object-Oriented Design – Banking Application


Unit 8: Refactoring and Code Smells

In this unit, I studied the concept of code smells, which are signs of problems in a codebase that affect readability, maintainability, and reliability. I learned how to detect these issues and apply refactoring techniques to improve software design while keeping the same functionality. The unit emphasized the importance of ongoing code improvement as part of sustainable software engineering.

Key outcomes include:

• Understanding what code smells are and why they occur.

• Applying refactoring techniques such as Extract Method, Replace Magic Numbers with Constants, and Replace Conditional with Polymorphism.

• Improving maintainability and readability through clean coding practices.

• Recognizing refactoring as a process that reduces technical debt and extends the life of software.

This unit built on earlier lessons on object-oriented design, SOLID principles, and design patterns, creating a solid foundation for later topics such as software architecture and system design.

Practical Exercise: Code Smell Analysis and Refactoring

Original Code

def calculate_total_price(items):
    total = 0
    for item in items:
        if item['type'] == 'book':
            total += item['price'] * 0.9  # 10% discount for books
        elif item['type'] == 'electronics':
            total += item['price'] * 0.8  # 20% discount for electronics
        else:
            total += item['price']
    return total

Identified Code Smells

Adding new product types increases code complexity.

Magic Numbers

Hardcoded discount factors (0.9, 0.8) reduce clarity and make maintenance difficult.

Any change requires modifying the function directly.

Long Method with Conditional Logic

The if-elif-else chain centralises discount logic, violating Open/Closed Principle.



Refactoring Techniques Applied


Option 1: Replace Magic Numbers with Constants

BOOK_DISCOUNT = 0.9
ELECTRONICS_DISCOUNT = 0.8

def calculate_total_price(items):
    total = 0
    for item in items:
        if item['type'] == 'book':
            total += item['price'] * BOOK_DISCOUNT
        elif item['type'] == 'electronics':
            total += item['price'] * ELECTRONICS_DISCOUNT
        else:
            total += item['price']
    return total



Option 2: Replace Conditional with Polymorphism (Strategy Pattern)

class DiscountStrategy:
    def apply_discount(self, price):
        return price

class BookDiscount(DiscountStrategy):
    def apply_discount(self, price):
        return price * 0.9

class ElectronicsDiscount(DiscountStrategy):
    def apply_discount(self, price):
        return price * 0.8

def calculate_total_price(items):
    discount_strategies = {
        'book': BookDiscount(),
        'electronics': ElectronicsDiscount()
    }
    total = 0
    for item in items:
        strategy = discount_strategies.get(item['type'], DiscountStrategy())
        total += strategy.apply_discount(item['price'])
    return total


Through this exercise, I learned how refactoring improves the structure of existing code without altering its external behaviour. Implementing the Strategy Pattern demonstrated how design patterns can eliminate conditionals and make systems easier to extend. Refactoring should be viewed as an iterative process and a constant part of software evolution that prevents technical debt and enhances long-term sustainability.

Unit 9: Object-Oriented Software Architecture

Object-Oriented Software Architecture for an E-Commerce Platform

Requirements
  1. Scalability: The system should handle a growing number of users and products.
  2. Modularity: The system should be divided into independent modules (e.g., user management, product catalogue, order processing).
  3. Security: The system must protect user data and transactions.
  4. Extensibility: The system should allow for easy addition of new features (e.g., payment methods, recommendation engines).
  5. Object-Oriented Architecture Design: The system will be designed using a layered architecture with the following layers:
    • Presentation Layer: Handles user interaction (e.g., web interface, mobile app).
    • Business Logic Layer: Implements core functionality (e.g., user authentication, product search, order processing).
    • Data Access Layer: Manages data storage and retrieval (e.g., databases, file systems). Each layer is further divided into modules, designed using object-oriented principles such as encapsulation, inheritance, and polymorphism.
Guidance
  1. Layered Architecture Overview.
  2. Modular Design with OOP:
    • User Management Module.
    • Product Catalog Module.
    • Order Processing Module.
  3. Security Practices:
    • Authentication.
  4. Scalability and Extensibility:
    • Dependency Injection.
    • Observer Pattern for Notifications.
  5. Database Design (Data Access Layer).

Repo Link to the above Project – https://github.com/nb25877/Python-OOP/tree/main/Unit9