IT242 Software Design and Development

Software Design and DevelopmentUnit 617 min read

OO Design & Patterns: Classes, Inheritance, SOLID, GoF Patterns

Unit 6 of Software Design and Development covers object-oriented design principles (encapsulation, inheritance, polymorphism), SOLID principles for maintainable code, and Gang of Four design patterns (creational, structural, behavioral) with UML diagrams and real-world applications in Nepalese software systems.

TAKEAWAYS:

  • Object-oriented design organizes software into reusable classes with relationships (inheritance, composition) to model real-world entities like Daraz’s product catalog or Ncell’s billing system.
  • SOLID principles (Single Responsibility, Open/Closed, Liskov Substitution, Interface Segregation, Dependency Inversion) prevent code rot in large systems like eSewa’s payment gateway.
  • Design patterns (Singleton for database connections, Observer for live traffic updates, Strategy for discount calculations) solve recurring problems in apps like Pathao’s ride-matching or NEPSE’s stock trading.
  • UML class diagrams visually represent inheritance hierarchies (e.g., Vehicle → Car → ElectricCar) and associations (e.g., User 1-to-many Order).
  • Refactoring legacy code using patterns improves performance (e.g., Flyweight for Daraz’s product thumbnails) and scalability (e.g., Proxy for lazy-loading Ncell’s customer data).
  • Exam questions test pattern identification in scenarios (e.g., "Which GoF pattern would you use for a Kathmandu traffic route optimizer?"), so memorize use cases and UML symbols.

Core Concepts: Classes, Objects, and the "Big Three" OO Principles

Object-Oriented Design (OOD) treats software as a network of interacting objects (instances of classes) that encapsulate data and behavior. Unlike procedural programming (where functions operate on data), OOD bundles data + methods into classes, enabling modularity and reusability.

1. Classes and Objects: The Building Blocks

  • A class is a blueprint (e.g., BankAccount), while an object is an instance (e.g., account123 of SBI_Nepal).
  • Attributes (data) + methods (functions) define a class’s interface.
  • Encapsulation: Hide internal state (e.g., private balance) and expose controlled access via methods (deposit(), withdraw()).
classDiagram
    class BankAccount {
        -accountNumber: String
        -balance: Double
        +deposit(amount: Double): void
        +withdraw(amount: Double): void
        +getBalance(): Double
    }
    class SavingsAccount {
        -interestRate: Double
        +calculateInterest(): Double
    }
    BankAccount <|-- SavingsAccount

Worked Example: Ncell’s Prepaid Plan

class PrepaidPlan {
    private double remainingBalance;
    private String customerID;

    public void recharge(double amount) {
        this.remainingBalance += amount;
    }

    public boolean makeCall(double duration) {
        if (remainingBalance >= duration * 0.50) {  // 0.50 NPR/sec
            remainingBalance -= duration * 0.50;
            return true;
        }
        return false;
    }
}
  • Encapsulation: remainingBalance is private; users interact only via recharge()/makeCall().
  • Real-world tie: Ncell’s app uses this to track usage and prevent over-charging.

Inheritance: "Is-A" Relationships

Inheritance lets a subclass inherit attributes/methods from a superclass (e.g., ElectricCar inherits from Car). Key terms:

  • extends: Defines inheritance (e.g., class ElectricCar extends Car).
  • @Override: Modifies inherited methods (e.g., calculateTax()).
  • super: Calls parent-class methods.
classDiagram
    class Vehicle {
        -maxSpeed: Double
        +startEngine(): void
    }
    class Car {
        -numDoors: Int
        +accelerate(): void
    }
    class ElectricCar {
        -batteryCapacity: Double
        +charge(): void
        +override accelerate(): void
    }
    Vehicle <|-- Car
    Car <|-- ElectricCar

Worked Example: Daraz’s Product Catalog

class Product {
    private String id;
    private double price;
    public double getPrice() { return price; }
}

class ElectronicProduct extends Product {
    private double warrantyPeriod;
    public double getPriceWithWarranty() {
        return super.getPrice() * 1.10;  // 10% markup for warranty
    }
}
  • Why? Daraz groups products hierarchically (e.g., ElectronicProduct adds warranty logic without duplicating price).
  • Real-world tie: Daraz’s search filters use inheritance to categorize products (e.g., Laptop → GamingLaptop).

Polymorphism: "One Interface, Many Forms"

Polymorphism allows objects of different classes to be treated as objects of a common superclass. Two types:

  1. Compile-time (Method Overloading): Same method name, different parameters.
    class Calculator {
        int add(int a, int b) { return a + b; }
        double add(double a, double b) { return a + b; }
    }
    
  2. Run-time (Method Overriding): Subclass provides specific implementation.
    class Shape { double area(); }
    class Circle extends Shape { double area() { return Math.PI * r * r; } }
    class Square extends Shape { double area() { return side * side; } }
    

Visual: Polymorphism in Action

sequenceDiagram
    participant UI as User Interface
    participant Shape as Shape (abstract)
    participant Circle as Circle
    participant Square as Square
    UI->>Shape: draw()
    Shape->>Circle: area()  # Runtime decision
    Shape->>Square: area()

Worked Example: NEPSE’s Stock Trading

interface Stock {
    double calculateValue();
}

class EquityStock implements Stock {
    public double calculateValue() { return shares * price; }
}

class DerivativeStock implements Stock {
    public double calculateValue() { return underlyingValue * multiplier; }
}
  • Why? NEPSE’s system processes EquityStock and DerivativeStock uniformly via the Stock interface.
  • Real-world tie: When you buy shares on NEPSE’s app, the system calls calculateValue() polymorphically.

SOLID Principles: The 5 Pillars of Maintainable Code

SOLID principles prevent "spaghetti code" in large systems. Memorize these for exam scenarios!

Principle Definition Example (Bad → Good)
Single Responsibility A class should have one reason to change. ❌ UserManager handles login, profile, and billing. <br> ✅ Split into AuthService, ProfileService, BillingService.
Open/Closed Open for extension, closed for modification. ❌ Modify Discount class to add new types. <br> ✅ Use Strategy pattern (see below).
Liskov Substitution Subtypes must be substitutable for their base types. ❌ Square breaks Rectangle if setWidth()/setHeight() are overridden separately.
Interface Segregation Clients shouldn’t depend on interfaces they don’t use. ❌ Worker interface forces clean() on Manager. <br> ✅ Split into Worker and Manager.
Dependency Inversion Depend on abstractions, not concretions. ❌ PaymentProcessor directly uses CreditCard. <br> ✅ Inject PaymentMethod interface.

Worked Example: eSewa’s Payment Gateway

// Violation: Dependency on concrete class
class PaymentProcessor {
    private CreditCard card;
    public void processPayment(double amount) { ... }
}

// Fix: Dependency Inversion
interface PaymentMethod { void pay(double amount); }
class CreditCard implements PaymentMethod { ... }
class Khalti implements PaymentMethod { ... }
class PaymentProcessor {
    private PaymentMethod method;
    public void processPayment(double amount) {
        method.pay(amount);  // Works with any PaymentMethod
    }
}
  • Why? eSewa supports multiple payment methods (credit card, Khalti, mobile banking) without modifying PaymentProcessor.

Design Patterns: The "Gang of Four" (GoF) Patterns

Design patterns are proven solutions to common problems. Categorized into 3 groups:

1. Creational Patterns: Object Creation Mechanisms

Pattern Purpose Example
Singleton Ensure a class has one instance (e.g., database connection). Database.getInstance() returns the same Connection object.
Factory Method Delegate instantiation to subclasses. VehicleFactory.createVehicle("car") returns Car or Truck.
Builder Construct complex objects step-by-step. PizzaBuilder.setToppings().setSauce().build().

Visual: Singleton Pattern

classDiagram
    class Database {
        -instance: Database
        +getInstance(): Database
        -Database() { /* private constructor */ }
    }

Worked Example: NTC’s Network Connection Pool

class NetworkConnection {
    private static NetworkConnection instance;
    private NetworkConnection() { /* setup */ }
    public static synchronized NetworkConnection getInstance() {
        if (instance == null) {
            instance = new NetworkConnection();
        }
        return instance;
    }
}
  • Why? NTC reuses a single connection pool for all users to avoid overhead.

2. Structural Patterns: Class/Object Composition

Pattern Purpose Example
Adapter Make incompatible interfaces work together. LegacyPrinterAdapter lets new apps use old printers.
Decorator Add responsibilities dynamically. Espresso → MilkDecorator → SugarDecorator.
Facade Simplify complex subsystems. HomeTheaterFacade hides DVDPlayer, Projector, SoundSystem.

Visual: Decorator Pattern

classDiagram
    class Coffee {
        +cost(): Double
    }
    class Espresso {
        +cost(): Double
    }
    class MilkDecorator {
        -coffee: Coffee
        +cost(): Double
    }
    class SugarDecorator {
        -coffee: Coffee
        +cost(): Double
    }
    Coffee <|-- Espresso
    Coffee <|-- MilkDecorator
    Coffee <|-- SugarDecorator

Worked Example: Pathao’s Ride Pricing

interface RideCost {
    double calculate();
}

class BaseCost implements RideCost {
    public double calculate() { return distance * 10; }
}

class SurgeDecorator implements RideCost {
    private RideCost rideCost;
    public SurgeDecorator(RideCost rc) { this.rideCost = rc; }
    public double calculate() { return rideCost.calculate() * 1.5; }  // 50% surge
}
  • Why? Pathao adds surge pricing dynamically without modifying BaseCost.

3. Behavioral Patterns: Object Interaction

Pattern Purpose Example
Observer Define 1-to-many dependencies (e.g., publish-subscribe). TrafficUpdate notifies all RideApps (Pathao, Uber) of delays.
Strategy Encapsulate interchangeable algorithms. SortingStrategy swaps QuickSort/MergeSort at runtime.
Command Encapsulate actions as objects. UndoCommand for text editors (like Microsoft Word).

Visual: Observer Pattern

sequenceDiagram
    participant TrafficSystem as TrafficUpdateSystem
    participant Pathao as Pathao App
    participant Uber as Uber App
    TrafficSystem->>Pathao: notify("Delays on Ring Road")
    TrafficSystem->>Uber: notify("Delays on Ring Road")

Worked Example: Ncell’s Roaming Alerts

interface Observer { void update(String message); }
class RoamingAlertSystem {
    private List<Observer> observers = new ArrayList<>();
    public void addObserver(Observer o) { observers.add(o); }
    public void sendAlert(String message) {
        for (Observer o : observers) {
            o.update(message);  // Calls Ncell App, SMS Gateway, etc.
        }
    }
}
  • Why? Ncell’s system notifies users via app, SMS, and email without coupling to specific notification methods.

UML Diagrams: Visualizing OO Design

UML (Unified Modeling Language) diagrams bridge theory and implementation. Key diagrams for this unit:

  1. Class Diagram: Shows classes, attributes, methods, and relationships.

    • Symbols:
      • + = public, - = private, # = protected.
      • --- = association, ---o = aggregation, ---⊳ = composition.
      • ←| = inheritance, ⋖ = interface.
  2. Sequence Diagram: Shows object interactions over time (e.g., protocol handshakes).

  3. State Diagram: Models object lifecycle (e.g., Order states: Placed → Shipped → Delivered).

Worked Example: Daraz Order Processing

stateDiagram-v2
    [*] --> Placed
    Placed --> Processing: payment confirmed
    Processing --> Shipped: inventory reserved
    Shipped --> Delivered: dispatched
    Delivered --> [*]
    Processing --> Cancelled: user request
    Shipped --> Cancelled: out of stock

Refactoring: Improving Legacy Code with Patterns

Refactoring applies patterns to existing code without changing functionality. Common scenarios:

  • Replace Conditional with Strategy: Replace if-else chains with polymorphism.
    // Before
    if (discountType == "STUDENT") { applyStudentDiscount(); }
    else if (discountType == "SENIOR") { applySeniorDiscount(); }
    
    // After (Strategy Pattern)
    interface DiscountStrategy { void apply(); }
    class StudentDiscount implements DiscountStrategy { ... }
    class SeniorDiscount implements DiscountStrategy { ... }
    
  • Introduce Adapter: Wrap legacy code to fit new interfaces.
  • Use Flyweight: Share common data (e.g., Daraz’s product images).

In the Real World

  1. eSewa’s Payment Gateway

    • Pattern: Strategy (for different payment methods: credit card, Khalti, mobile banking).
    • How: The system injects a PaymentMethod object (e.g., KhaltiPayment) at runtime, letting users switch without changing the core PaymentProcessor.
    • Nepali tie: When you pay utility bills via eSewa, the app dynamically selects the strategy based on your chosen method.
  2. Pathao’s Ride-Matching Algorithm

    • Pattern: Observer (for real-time traffic updates) + Singleton (for the central dispatch system).
    • How:
      • The TrafficUpdateSystem (Singleton) notifies all ride apps (Observer) of delays.
      • The dispatch system (Singleton) ensures one instance manages all ride assignments.
    • Nepali tie: During Kathmandu traffic jams, Pathao reroutes drivers using these patterns to minimize wait times.
  3. NEPSE’s Stock Trading Platform

    • Pattern: Factory Method (for creating different order types) + Command (for undoing trades).
    • How:
      • OrderFactory.createOrder("BUY", "NEPSE:1") returns a BuyOrder or SellOrder.
      • Traders can undo actions via UndoCommand, stored in a stack.
    • Nepali tie: When you place a trade on NEPSE’s website, the system uses these patterns to handle your order and allow cancellations.
  4. Daraz’s Product Catalog

    • Pattern: Composite (for product hierarchies) + Flyweight (for shared product images).
    • How:
      • ElectronicProduct inherits from Product but adds warrantyPeriod.
      • Thumbnail images are stored once (Flyweight) and reused across listings.
    • Nepali tie: When you browse Daraz, the site loads product images efficiently and categorizes items hierarchically.

Exam Tip

  1. Pattern Identification: Exams often ask:

    • "Which GoF pattern would you use for a [scenario]?"
    • Example: "Design a system where users can subscribe to live traffic updates." Answer: Observer Pattern (TrafficUpdateSystem notifies subscribers like Pathao/Uber).
    • Memorize use cases:
      • Need one instance? → Singleton.
      • Need dynamic behavior? → Strategy.
      • Need event handling? → Observer.
  2. UML Questions: Draw diagrams from descriptions. For example:

    • "Show a class diagram for a Vehicle hierarchy with Car and ElectricCar." Must include: Inheritance arrow (<|--), attributes (maxSpeed), and methods (accelerate()).
  3. SOLID Violations: Spot anti-patterns in code snippets. For example:

    • "This class handles both user authentication and database logging. Which SOLID principle is violated?" Answer: Single Responsibility Principle (SRP).
  4. Real-World Scenarios: Relate patterns to Nepali apps. For example:

    • "How would you implement Khalti’s payment integration in eSewa using design patterns?" Answer:
      • Use Strategy Pattern to switch between KhaltiPayment, CreditCardPayment, etc.
      • Use Observer Pattern to notify users of payment status updates.
  5. Code Snippets: Be ready to write short implementations. For example:

    • "Implement the Singleton pattern for a DatabaseConnection class."
      public class DatabaseConnection {
          private static DatabaseConnection instance;
          private DatabaseConnection() {}
          public static DatabaseConnection getInstance() {
              if (instance == null) {
                  instance = new DatabaseConnection();
              }
              return instance;
          }
      }
      
  6. Diagram Labels: In UML questions, label every arrow and box. For example:

    • Inheritance: Car <|-- ElectricCar (with ElectricCar above Car).
    • Association: User --- Order (with multiplicity 1 to *).

Pro Tip: Create a cheat sheet with:

  • GoF patterns table (creational/structural/behavioral).
  • SOLID acronym with one-line definitions.
  • UML symbols (e.g., ---o = aggregation).
  • Nepali app examples (eSewa = Strategy, Pathao = Observer).

Based on the TU BITM syllabus for Software Design and Development (IT242), unit 6.

Discussion

Loading…