Software EngineeringUnit 613 min read

OO Design & Patterns: Classes, UML, SOLID, Gang of Four

Unit 6 of Software Engineering covers object-oriented design principles (encapsulation, inheritance, polymorphism), UML class diagrams, design patterns (creational, structural, behavioral), and their application in real-world systems like eSewa’s payment flow or Daraz’s order routing.

TAKEAWAYS:

  • Classes and objects model real-world entities with attributes (data) and methods (behavior), visualized in UML class diagrams.
  • Design patterns (e.g., Singleton, Observer, Factory) solve recurring problems in OO design, improving code reusability and maintainability.
  • SOLID principles (Single Responsibility, Open/Closed, Liskov Substitution) ensure robust, scalable OO architectures.
  • UML diagrams (class, sequence, state) bridge analysis and design, helping teams communicate system structure clearly.
  • Real-world ties: Patterns like Observer power live updates in WhatsApp messages, while Strategy lets Pathao choose optimal delivery routes dynamically.

Core Concepts: Classes, Objects, and UML

1. Classes and Objects

Definition:

  • A class is a blueprint for objects, defining attributes (data) and methods (functions). An object is an instance of a class.
  • Example: In a banking system, Account is a class with attributes accountNumber, balance, and methods deposit(), withdraw(). An object like account123 is a specific bank account.

Key Properties:

  • Encapsulation: Bundling data and methods that operate on the data, restricting direct access via access modifiers (private, public, protected).
  • Inheritance: A mechanism where a new class (child) inherits properties/methods from an existing class (parent). Example: SavingsAccount inherits from Account.
  • Polymorphism: Objects of different classes can be treated as objects of a common superclass. Example: A Shape class with methods draw() can have subclasses Circle and Rectangle, each implementing draw() differently.

UML Class Diagram:

classDiagram
    class Account {
        -accountNumber: String
        -balance: Double
        +deposit(amount: Double)
        +withdraw(amount: Double)
    }
    class SavingsAccount {
        -interestRate: Double
        +calculateInterest()
    }
    Account <|-- SavingsAccount

Real-world tie: In eSewa, the Payment class handles transactions, while MobilePayment (child of Payment) adds SMS-based authentication.

2. UML Diagrams for OO Design

UML (Unified Modeling Language) provides visual tools to model systems. For this unit, focus on:

  • Class Diagrams: Show classes, their attributes, methods, and relationships (inheritance, association, aggregation, composition).
  • Sequence Diagrams: Model interactions between objects over time (e.g., a user ordering from Daraz).
  • State Diagrams: Represent object lifecycle (e.g., an order in Daraz: Placed → Processing → Shipped).

Example: Daraz Order Processing (Sequence Diagram)

sequenceDiagram
    participant User
    participant Order
    participant Payment
    participant Inventory
    User->>Order: placeOrder(itemList)
    Order->>Payment: processPayment(amount)
    Payment-->>Order: PaymentSuccess
    Order->>Inventory: checkStock(itemList)
    Inventory-->>Order: StockAvailable
    Order->>User: confirmOrder(orderID)

Real-world tie: Pathao’s ride-hailing system uses sequence diagrams to model driver-passenger interactions (e.g., User requests ride → Driver accepts → Navigation updates route).


SOLID Principles: Writing Maintainable Code

SOLID is an acronym for five design principles to improve OO design:

Principle Description Example
Single Responsibility A class should have one reason to change (one responsibility). User class handles authentication; Order class manages orders.
Open/Closed Classes should be open for extension but closed for modification. Use inheritance/interface to add features (e.g., Payment interface with CreditCardPayment, MobilePayment).
Liskov Substitution Subclasses must be substitutable for their parent classes without breaking behavior. A Square class should not break Rectangle methods (e.g., area()).
Interface Segregation Clients should not be forced to depend on interfaces they don’t use. Split IPayment into ICreditCardPayment and IMobilePayment.
Dependency Inversion High-level modules should not depend on low-level modules; both should depend on abstractions. OrderProcessor depends on IPayment, not CreditCardPayment directly.

Worked Example: Ncell’s Billing System

  • Problem: The Billing class handles both Prepaid and Postpaid plans, violating Single Responsibility.
  • Solution: Refactor into:
classDiagram
    class Billing {
        +generateBill(user: User)
    }
    class Plan {
        <<interface>>
        +calculateBill(usage: Double)
    }
    class PrepaidPlan {
        +calculateBill(usage: Double)
    }
    class PostpaidPlan {
        +calculateBill(usage: Double)
    }
    Billing --> Plan
    PrepaidPlan ..|> Plan
    PostpaidPlan ..|> Plan
  • Benefit: Adding a new plan (e.g., CorporatePlan) requires extending Billing without modifying it.

Design Patterns: Reusable Solutions

Design patterns are proven solutions to common OO problems. They are categorized into three groups:

SingletonFactory MethodBuilderCreationalAdapterDecoratorFacadeStructuralObserverStrategyCommandBehavioralDesign Patterns
Gang of Four Design Pattern Categories

1. Creational Patterns

Purpose: Handle object creation.

Pattern Use Case Example
Singleton Ensure a class has only one instance (e.g., configuration manager). DatabaseConnection in eSewa to manage a single DB link.
Factory Delegate object creation to subclasses. PaymentFactory creates CreditCardPayment or MobilePayment objects.
Builder Construct complex objects step-by-step. OrderBuilder in Daraz to assemble orders with items, addresses, etc.

Singleton Example (eSewa’s Config Manager):

public class ConfigManager {
    private static ConfigManager instance;
    private Properties config;

    private ConfigManager() { config = loadConfig(); }

    public static ConfigManager getInstance() {
        if (instance == null) {
            instance = new ConfigManager();
        }
        return instance;
    }
}

2. Structural Patterns

Purpose: Organize relationships between classes.

Pattern Use Case Example
Adapter Make incompatible interfaces work together. LegacyPaymentAdapter to integrate old payment systems with eSewa.
Decorator Add responsibilities to objects dynamically. EncryptedOrder decorator adds encryption to Order objects.
Facade Simplify complex subsystems. PaymentFacade in Khalti to hide low-level payment processing.

Decorator Example (Daraz Order Encryption):

Orderprocess()EncryptionDecoratorprocess() + encrypt()
Decorator Pattern: Adding Encryption to Order Processing

3. Behavioral Patterns

Purpose: Define communication between objects.

Pattern Use Case Example
Observer Notify objects of state changes (e.g., event handling). WhatsApp notifies users of new messages via Observer pattern.
Strategy Encapsulate interchangeable algorithms. Pathao uses DeliveryStrategy to choose fastest/cheapest route.
Command Encapsulate requests as objects. Daraz’s PlaceOrderCommand to undo/redo orders.

Observer Example (WhatsApp Messages):

classDiagram
    class Message {
        -observers: List~Observer~
        +attach(observer: Observer)
        +notify()
    }
    class User {
        +update(message: String)
    }
    Message "1" *-- "0..*" User : observes

Real-world tie: NEPSE’s stock price updates use the Observer pattern to notify subscribers of price changes.


Comparing Patterns and SOLID

Feature Design Patterns SOLID Principles
Scope Specific solutions to common problems. Broad guidelines for good OO design.
Example Singleton, Observer. Single Responsibility, Open/Closed.
When to Use When you need a proven solution. When designing classes/interfaces.
Focus Behavior/structure of objects. Principles for maintainable code.

Worked Example: Kathmandu Traffic Routes (Strategy Pattern)

  • Problem: Traffic routes change based on time/day (e.g., rush hour vs. night).
  • Solution: Use RouteStrategy interface with implementations:
    interface RouteStrategy {
        String calculateRoute();
    }
    class RushHourStrategy implements RouteStrategy { ... }
    class NightStrategy implements RouteStrategy { ... }
    
  • Benefit: Switch strategies dynamically without modifying route logic.

UML in Practice: Modeling a System

Case Study: Khalti Payment Flow

  1. Class Diagram:
    classDiagram
        class User {
            +id: String
            +balance: Double
        }
        class Payment {
            +amount: Double
            +process()
        }
        class MobilePayment {
            +phoneNumber: String
            +authenticate()
        }
        User "1" --> "1" Payment
        Payment <|-- MobilePayment
  2. Sequence Diagram (Payment Process):
    sequenceDiagram
        participant User
        participant KhaltiApp
        participant PaymentGateway
        User->>KhaltiApp: InitiatePayment(amount)
        KhaltiApp->>PaymentGateway: RequestPayment(amount)
        PaymentGateway-->>KhaltiApp: PaymentSuccess/Failed
        KhaltiApp->>User: ShowResult()

Real-world tie: NTC’s billing system uses UML to model interactions between Customer, Bill, and Payment classes.


In the Real World

  1. eSewa’s Payment System:

    • Uses the Observer pattern to notify users of transaction status updates (e.g., PaymentSuccess, PaymentFailed).
    • Applies Singleton for the ConfigManager to ensure consistent settings across all transactions.
  2. Pathao’s Route Optimization:

    • Employs the Strategy pattern to dynamically select the best delivery route based on traffic, distance, or driver availability.
    • Follows Open/Closed Principle to add new route algorithms (e.g., EcoRouteStrategy) without modifying existing code.
  3. Daraz’s Order Processing:

    • Uses Builder pattern to construct complex orders with items, addresses, and payment details step-by-step.
    • Implements Facade pattern to simplify the checkout process for users (hiding underlying inventory/payment systems).

Exam Tip

  1. Diagrams are worth marks: Always draw UML class/sequence diagrams for case studies. Label relationships (e.g., inheritance, association) and include attributes/methods.
  2. Pattern applications: For questions like “How would you design X?”, tie your answer to 1–2 patterns (e.g., “Use Observer for live updates in WhatsApp”).
  3. SOLID violations: Exams often ask to “Identify and fix SOLID violations”. Practice refactoring code snippets (e.g., splitting a monolithic class).
  4. Real-world mapping: Relate abstract concepts to local apps (e.g., “Khalti uses Decorator to add fraud checks to payments”).
  5. Trace examples: For sequence diagrams, trace the flow step-by-step (e.g., “User → Order → Payment → Inventory → User”).
violates Single Responsibilityviolates Single Responsibilityviolates Single ResponsibilityMonolithicClassClassAClassBClassC
SOLID Violation Example: Monolithic Class Structure

Based on the PU BE Computer (PU) syllabus for Software Engineering (CMP348), unit 6.

Discussion

Loading…