SOFTWARE MODELING & DESIGN
Chapter 5: Software Design Principles and Patterns
GRASP Principles Creational Patterns Structural Patterns Behavioral Patterns
1: INTRODUCTION AND NEED FOR DESIGN PRINCIPLES
1.1 What are Software Design Principles?
Software Design Principles are fundamental guidelines that help software designers make good
decisions during the design phase. They are not rules you must follow mechanically — they are wisdom
distilled from decades of OO software engineering experience that helps you build systems that are
maintainable, flexible, extensible, and testable.
📖 Software Design Principles
Software Design Principles are general guidelines for structuring classes, assigning
responsibilities, and designing relationships in an object-oriented system.
They answer the question: 'How should I assign responsibilities to classes and design their
relationships for maximum flexibility and minimum rework?'
Two major sets of principles:
1. GRASP — General Responsibility Assignment Software Patterns (9 patterns by Craig
Larman)
2. GOF Design Patterns — 23 classic patterns by the 'Gang of Four' (Gamma, Helm,
Johnson, Vlissides)
1.2 Why Do We Need Design Principles?
Without design principles, OO code quickly degrades into a tangled mess that is difficult to change,
test, or reuse. The most common problems in poorly designed systems:
Problem (Bad Symptom Caused By
Design)
Rigidity Every change requires many Too many dependencies between classes
other changes in unrelated
places
Fragility When you fix one bug, two Fragile, tightly-coupled classes
others appear elsewhere
Immobility Cannot reuse a useful class in Classes know too much about each other
a new project — too many
dependencies
Viscosity It is easier to hack a Poor architecture that makes correct
workaround than to do the changes harder than wrong ones
right thing
Needless Complexity Infrastructure for things that Over-engineering, premature abstraction
may never be needed
Needless Repetition Same code logic copy-pasted Lack of abstraction; duplicate
in multiple places responsibilities
Opacity Code is hard to read and Poor naming, unclear responsibilities, no
understand documentation
📝 The Core Goal of Design Principles
All design principles ultimately aim for the same goal:
HIGH COHESION within classes + LOW COUPLING between classes.
Cohesion: Each class does one thing well and all its parts work toward the same purpose.
Coupling: Classes depend on each other as little as possible, and only through stable
interfaces.
A well-designed system is easy to change because changes stay local — one change in one
class.
2: GRASP — GENERAL RESPONSIBILITY ASSIGNMENT
SOFTWARE PATTERNS
2.1 Introduction to GRASP
GRASP (General Responsibility Assignment Software Patterns) is a set of 9 fundamental principles for
assigning responsibilities to classes in an OO design. Introduced by Craig Larman in 'Applying UML
and Patterns', GRASP helps answer the fundamental OOD question: 'Which class should be
responsible for this?'
📖 GRASP Patterns
GRASP patterns are guidelines for assigning responsibilities (knowing and doing) to classes
and objects during OO design.
They are called 'patterns' because they recur across many different domains and systems.
GRASP patterns are used DURING design — they guide decisions about which class should
do what.
The 9 GRASP patterns are: Creator, Information Expert, Low Coupling, High Cohesion,
Controller, Polymorphism, Pure Fabrication, Indirection, Protected Variations.
2.2 GRASP 1: Creator
📖 Creator Pattern
PROBLEM: Who should be responsible for creating a new instance of class B?
SOLUTION: Assign class A the responsibility of creating an instance of B if ONE OR MORE
of the following is true:
1. A 'contains' or aggregates B (A has a collection of B objects)
2. A 'records' B (A keeps a log or history of B objects)
3. A 'closely uses' B (A frequently calls methods on B)
4. A has the 'initializing data' for B (A has all the data needed to create B)
BENEFIT: Supports Low Coupling — B is created by whoever naturally owns or uses it.
💡 Creator Pattern — E-Commerce System
QUESTION: Who should create an OrderItem object?
Applying Creator criteria:
1. Does Order contain/aggregate OrderItem? YES — Order has a collection of OrderItems.
2. Does Order record OrderItems? YES — Order keeps track of all its items.
3. Does Order closely use OrderItem? YES — Order calls calculateTotal() using OrderItem
data.
4. Does Order have the data to create OrderItem? YES — it has productId, quantity, price.
DECISION: Order is the Creator of OrderItem.
Implementation: [Link](product, qty) { [Link](new OrderItem(product, qty)); }
COUNTEREXAMPLE (Bad Design):
Having a separate OrderItemFactory that creates OrderItems but has no other connection
to them.
This adds unnecessary coupling without any benefit.
More Creator Examples:
Sale creates SaleLineItem (Sale aggregates SaleLineItems)
Library creates LibraryMember (Library records/tracks Members)
Invoice creates InvoiceLine (Invoice contains InvoiceLines)
2.3 GRASP 2: Information Expert
📖 Information Expert Pattern
PROBLEM: What is the most basic principle for assigning responsibilities to classes?
SOLUTION: Assign a responsibility to the class that has the information (data) necessary to
fulfill it.
The class that 'knows' the data is the class that should 'do' the work with that data.
BENEFIT: Keeps data and behavior together — promotes encapsulation and reduces the
need to expose data.
This is the most widely used GRASP pattern — it is the foundation of all OO design.
💡 Information Expert — Library Management System
QUESTION 1: Who should calculate the total fine for an overdue book?
Data needed: borrowDate, dueDate, dailyFineRate, returnDate
Who has this data? The Loan class (it records borrow/due/return dates)
Also: FinePolicy has dailyFineRate
DECISION: Loan calculates fine using [Link]()
[Link]() { days = returnDate - dueDate; return days * [Link](); }
QUESTION 2: Who should know the total price of a shopping cart?
Data needed: all CartItem prices and quantities
Who has this? Cart contains CartItems; CartItem has price and quantity
DECISION: Cart calculates total by delegating to each CartItem
[Link]() { return [Link]().mapToDouble(CartItem::getLineTotal).sum(); }
[Link]() { return unitPrice * quantity; }
(Cart is Expert for total; CartItem is Expert for line total)
KEY INSIGHT: Often, fulfilling a responsibility is SPREAD across multiple experts.
Each class does its part — the task is completed by collaboration, not by one god-class.
2.4 GRASP 3: Low Coupling
📖 Low Coupling Pattern
PROBLEM: How can we minimize the impact of changes? How can we increase reuse?
SOLUTION: Assign responsibilities so that coupling remains low. Prefer designs that do not
increase coupling.
COUPLING = the degree to which one class depends on another class.
HIGH COUPLING is bad: changes in one class force changes in dependent classes; classes
cannot be reused independently.
LOW COUPLING is good: classes are independent, changes stay local, classes are
reusable.
BENEFIT: Reduced impact of change; increased reuse; easier to understand classes in
isolation.
Type of Coupling Description Severity
Content Coupling (worst) Class A directly modifies data inside Very High — avoid always
Class B
Common Coupling Two classes share the same global High — avoid
data/variable
Control Coupling Class A tells Class B how to do its job Medium — minimize
(passes flags/modes)
Stamp/Data Coupling Classes share complex data structures Low-Medium — acceptable
or pass objects
Message Coupling (best) Classes communicate only through Low — aim for this
method calls with simple parameters
💡 Low Coupling — Payment Processing
HIGH COUPLING design (bad):
class Order {
void checkout() {
CreditCardProcessor p = new CreditCardProcessor(); // direct dependency
[Link](cardNumber, cvv, amount); // knows too much
}
}
Problem: Order is tightly coupled to CreditCardProcessor. Cannot use UPI or Cash.
LOW COUPLING design (good — using interface):
interface IPaymentGateway { boolean processPayment(double amount); }
class Order {
private IPaymentGateway gateway; // depends on INTERFACE, not concrete class
void checkout() { [Link](totalAmount); }
}
class CreditCardGateway implements IPaymentGateway { ... }
class UPIGateway implements IPaymentGateway { ... }
Now Order is coupled only to the stable interface IPaymentGateway.
New payment methods can be added without changing Order at all.
2.5 GRASP 4: High Cohesion
📖 High Cohesion Pattern
PROBLEM: How can we keep classes focused, understandable, and manageable?
SOLUTION: Assign responsibilities so that cohesion remains high. A class should have a
small, focused, related set of responsibilities.
COHESION = the degree to which the responsibilities of a class are strongly related and
focused.
HIGH COHESION: The class does one thing well. All its methods and attributes serve a
single clear purpose.
LOW COHESION: The class does too many unrelated things — a 'God class'. Hard to
understand, maintain, and reuse.
BENEFIT: Easier to understand, maintain, and reuse. Changes are localized.
💡 High Cohesion — Employee Management System
LOW COHESION (bad) — 'God Class':
class EmployeeManager {
void hireEmployee() { ... }
void fireEmployee() { ... }
void calculateSalary() { ... }
void generatePayslip() { ... }
void sendEmailNotification() { ... }
void backupDatabase() { ... } // what does this have to do with employees??
void generateTaxReport() { ... }
void updateAttendance() { ... }
}
This class has 8 unrelated responsibilities. Any change risks breaking something else.
HIGH COHESION (good) — focused classes:
class Employee { hireEmployee(), fireEmployee(), updateDetails() }
class SalaryCalculator { calculateSalary(), generatePayslip() }
class NotificationService { sendEmailNotification(), sendSMSAlert() }
class AttendanceTracker { updateAttendance(), getAttendanceReport() }
class TaxReportGenerator { generateTaxReport(), exportToExcel() }
Each class has ONE focused responsibility. Easy to understand and change independently.
2.6 GRASP 5: Controller
📖 Controller Pattern
PROBLEM: Which class (beyond the UI layer) should receive and handle system events?
SOLUTION: Assign the responsibility of receiving/handling a system operation to a Controller
class.
A Controller is the FIRST object beyond the UI that handles system input events.
TWO types of Controllers:
1. Facade Controller (or System Controller): One class handles ALL system operations.
Used for small systems.
Example: SystemController or ApplicationController handles everything.
2. Use Case Controller: One separate controller class per use case or use case family.
Example: LoginController, CheckoutController, InventoryController.
Controllers should DELEGATE work to other classes — they should NOT do the work
themselves (that violates High Cohesion).
💡 Controller Pattern — Online Exam System
System Events to handle: startExam(), submitAnswer(), endExam(), calculateResult()
OPTION 1 — Facade Controller (one controller for everything):
class ExamSystemController {
void startExam(studentId, examId) { ... }
void submitAnswer(qId, answer) { ... }
void endExam(examId) { ... }
void calculateResult(studentId) { ... }
}
OK for simple systems, but becomes bloated as system grows.
OPTION 2 — Use Case Controllers (preferred for complex systems):
class ExamSessionController { // handles startExam, endExam
void startExam(studentId, examId) {
exam = [Link](examId); // delegates to Expert
session = new ExamSession(student, exam); // delegates to Creator
[Link]();
}
}
class AnswerController { void submitAnswer(qId, answer) { ... } }
class ResultController { void calculateResult(studentId) { ... } }
Each controller handles ONE use case family. Thin controllers — they delegate, not do.
2.7 GRASP 6: Polymorphism
📖 Polymorphism Pattern
PROBLEM: How do we handle alternatives that vary by type? How can we replace parts of a
system without affecting other parts?
SOLUTION: When related alternatives or behaviors vary by type, assign responsibility to the
types themselves using polymorphic operations (overriding methods).
Instead of: if (type == 'A') { doX(); } else if (type == 'B') { doY(); }
Use: [Link]() — where each subtype overrides doOperation() with its own
behavior.
BENEFIT: New types can be added without changing existing code (Open/Closed Principle).
Eliminates long if/switch chains.
💡 Polymorphism Pattern — Tax Calculation System
WITHOUT Polymorphism (bad — if/switch chain):
class TaxCalculator {
double calculate(Employee emp) {
if ([Link]('Regular')) { return [Link] * 0.20; }
else if ([Link]('Senior')) { return [Link] * 0.25; }
else if ([Link]('Contract')) { return [Link] * 0.10; }
// Adding new type requires modifying this method!
}
}
WITH Polymorphism (good):
abstract class Employee {
abstract double calculateTax(); // polymorphic operation
}
class RegularEmployee extends Employee {
double calculateTax() { return salary * 0.20; }
}
class SeniorEmployee extends Employee {
double calculateTax() { return salary * 0.25; }
}
class ContractEmployee extends Employee {
double calculateTax() { return salary * 0.10; }
}
Usage: [Link]() // works for ALL types without any if/switch
Adding 'Intern' type: just add InternEmployee extends Employee — no existing code
changes!
2.8 GRASP 7: Pure Fabrication
📖 Pure Fabrication Pattern
PROBLEM: What to do when you need to assign a responsibility but no natural domain class
is a good fit, and assigning it would violate High Cohesion or Low Coupling?
SOLUTION: Assign the responsibility to a new, artificially invented class (Pure Fabrication)
that does not represent any real-world domain concept.
A Pure Fabrication is a class invented for the convenience of the software design — it has no
counterpart in the real world.
Examples: Repository classes, Service classes, Helper/Utility classes, Logger,
DataAccessObject.
BENEFIT: Maintains High Cohesion in domain classes; highly cohesive and reusable support
classes.
💡 Pure Fabrication — Database Persistence
PROBLEM: Who should save an Order to the database?
Option 1 — Give it to Order class (violates High Cohesion):
class Order {
void save() { Connection c = [Link](...); ... }
void load(int id) { ... SQL query ... }
}
BAD: Order now knows about SQL, JDBC, database schemas.
Order's job is to represent a business order, not to talk to databases!
Option 2 — Pure Fabrication: Create OrderRepository
class OrderRepository { // PURE FABRICATION — no real-world Order concept
void save(Order o) { ... SQL INSERT ... }
Order findById(int id) { ... SQL SELECT ... }
List<Order> findByStatus(String status) { ... }
}
OrderRepository has no counterpart in the real world.
It exists purely to serve the software's need to persist Order objects.
Order class stays clean and focused on business logic.
Other Pure Fabrications: EmailService, PDFGenerator, Logger, AuthenticationManager
2.9 GRASP 8: Indirection
📖 Indirection Pattern
PROBLEM: How can we avoid direct coupling between classes while still allowing them to
interact?
SOLUTION: Assign the responsibility of mediating between components to an intermediate
object (Indirection object) to avoid direct coupling.
The Indirection pattern introduces a 'middle-man' class that decouples two classes that need
to communicate.
BENEFIT: Low Coupling — neither side knows about the other directly. Both sides can
change independently.
Many other patterns are built on Indirection: Adapter, Facade, Proxy, Mediator.
💡 Indirection Pattern — Tax Rate Service
WITHOUT Indirection (tight coupling):
class Invoice { void calculate() { rate = [Link](); } }
Invoice is directly coupled to GovtTaxAPI. If the API changes, Invoice breaks.
WITH Indirection — introduce TaxService:
class TaxService { // THE INDIRECTION OBJECT
double getCurrentTaxRate() {
return [Link](); // wraps the external API
}
}
class Invoice {
private TaxService taxService; // depends on stable TaxService
void calculate() { rate = [Link](); }
}
Invoice ---> TaxService ---> GovtTaxAPI
If GovtTaxAPI changes interface, only TaxService needs to change.
Invoice is protected from change — it talks only to TaxService.
2.10 GRASP 9: Protected Variations
📖 Protected Variations Pattern
PROBLEM: How do we design objects and systems so that variations or instability in some
elements does not have an undesirable impact on other elements?
SOLUTION: Identify points of predicted variation or instability; assign responsibilities to
create a stable interface around them.
Wrap unstable or variable parts behind a stable interface. Other classes depend only on the
interface, not the implementation.
This is the most general and powerful GRASP pattern — it is the foundation of many design
patterns and SOLID principles.
BENEFIT: Changes in the varying part do NOT affect classes that use the stable interface.
💡 Protected Variations — Notification System
VARIATION POINT: The notification method may change (Email today, Push tomorrow, SMS
next).
WITHOUT Protected Variations:
class OrderService { void notifyCustomer() { [Link](...); } }
Every time notification method changes, OrderService must be rewritten.
WITH Protected Variations — stable interface wraps the variation:
interface INotificationChannel { // STABLE INTERFACE
void send(String recipient, String message);
}
class EmailNotification implements INotificationChannel { ... }
class SMSNotification implements INotificationChannel { ... }
class PushNotification implements INotificationChannel { ... }
class OrderService {
private INotificationChannel notifier; // depends only on stable interface
void notifyCustomer() { [Link]([Link], 'Your order is ready'); }
}
OrderService is PROTECTED from variations in notification technology.
New channel: just implement INotificationChannel — OrderService never changes.
Other Protected Variations: Data source variations (DB vs File), UI variations, Algorithm
variations
2.11 GRASP Summary
GRASP Pattern Core Question Solution in One Line Key Benefit
Creator Who creates object B? Assign to A if A contains, Low Coupling
records, closely uses, or
has data for B
Information Expert Who should do X? Assign to class that has Encapsulation,
the information needed to cohesion
do X
Low Coupling How to minimize change Assign so dependencies Resilience to
impact? are minimal and stable change
High Cohesion How to keep classes Assign so each class has Understandability,
focused? one focused responsibility reuse
Controller Who handles system Dedicated controller class Separates UI
events? (facade or use-case) from logic
Polymorphism How to handle type Use overriding instead of Open to
variations? if/switch type checks extension
Pure Fabrication No domain class fits? Invent a helper/service Maintains domain
class (not from domain) purity
Indirection How to avoid direct Introduce intermediate Decoupling
coupling? object to mediate
Protected Variations How to shield from Wrap variation points with Protection from
instability? stable interfaces change
3: INTRODUCTION TO GOF DESIGN PATTERNS
3.1 What are Design Patterns?
Design Patterns are reusable solutions to commonly occurring problems in software design. They are
not finished code — they are templates or blueprints that can be applied to a specific problem.
📖 GOF Design Patterns
The 23 GOF (Gang of Four) Design Patterns were documented in the landmark book 'Design
Patterns: Elements of Reusable Object-Oriented Software' (1994) by Erich Gamma, Richard
Helm, Ralph Johnson, and John Vlissides.
Each pattern has: a Name, Problem it solves, Solution (structure), and Consequences (trade-
offs).
Patterns are organized into 3 categories based on their purpose:
1. Creational Patterns — deal with OBJECT CREATION mechanisms
2. Structural Patterns — deal with COMPOSING CLASSES AND OBJECTS
3. Behavioral Patterns — deal with COMMUNICATION AND RESPONSIBILITY between
objects
Category Purpose Patterns in Syllabus All 23 GOF Patterns
Creational How objects are created; Singleton, Factory Abstract Factory, Builder,
hide creation details Method Prototype, Singleton,
Factory Method
Structural How classes/objects are Adapter, Facade Adapter, Bridge,
composed into larger Composite, Decorator,
structures Facade, Flyweight, Proxy
Behavioral How objects communicate Strategy, State Chain of Responsibility,
and distribute Command, Interpreter,
responsibility Iterator, Mediator,
Memento, Observer,
State, Strategy, Template
Method, Visitor
3.2 Why Use Design Patterns?
• Common vocabulary — 'use Strategy pattern here' communicates a complete design idea
instantly to other developers
• Proven solutions — patterns have been tested in thousands of real systems; they work
• Flexible designs — patterns promote loose coupling and high cohesion by design
• Reusable code structure — applying a pattern gives you a proven architecture to implement
• Easier communication — patterns provide a shared language for team design discussions
4: CREATIONAL PATTERNS
4.1 Singleton Pattern
📖 Singleton Pattern
INTENT: Ensure that a class has ONLY ONE INSTANCE and provide a GLOBAL POINT OF
ACCESS to it.
PROBLEM: Some classes should have exactly one instance — a second instance would
cause incorrect behavior.
Examples: Database connection pool, Application logger, Configuration manager, Thread
pool, Print spooler.
SOLUTION: The class itself controls its instantiation — makes the constructor private and
exposes a static method to get the single instance.
CATEGORY: Creational
Structure and Participants
Participant Role
Singleton class Defines private static instance variable; provides getInstance() static
method; has private constructor
Client Accesses the Singleton only through getInstance() — never via new
Singleton()
📊 Singleton Class Diagram
+======================================+
| Singleton |
+======================================+
| - instance: Singleton {static} | <- private static instance
| - (other attributes) |
+======================================+
| - Singleton() | <- PRIVATE constructor
| + getInstance(): Singleton {static} | <- public static factory
method
| + operation() |
+======================================+
getInstance() logic:
if (instance == null) { instance = new Singleton(); }
return instance;
💡 Singleton — Database Connection Manager
public class DatabaseConnection {
private static DatabaseConnection instance = null; // single instance
private Connection connection;
private DatabaseConnection() { // private constructor
connection = [Link]('jdbc:mysql://localhost/db', 'user', 'pass');
}
public static DatabaseConnection getInstance() { // global access point
if (instance == null) {
instance = new DatabaseConnection(); // created only ONCE
}
return instance;
}
public Connection getConnection() { return connection; }
}
// Usage — always returns the SAME instance:
DatabaseConnection db1 = [Link]();
DatabaseConnection db2 = [Link]();
// db1 == db2 (same object!)
Other Singleton examples: [Link](), [Link](),
[Link](), [Link]()
📝 Thread Safety — Singleton in Multi-Threaded Systems
The basic Singleton is NOT thread-safe. Two threads can both enter getInstance()
simultaneously and create two instances.
Fix 1 — Synchronized method: public static synchronized DatabaseConnection getInstance()
{ ... }
Problem: Synchronization overhead on EVERY call, even after instance is created.
Fix 2 — Double-Checked Locking (preferred):
if (instance == null) { synchronized([Link]) {
if (instance == null) { instance = new DatabaseConnection(); } } }
Fix 3 — Eager Initialization (simplest): private static DatabaseConnection instance = new
DatabaseConnection();
The instance is created at class loading time — always thread-safe, but uses memory even
if never used.
Fix 4 — Enum Singleton (best in Java): public enum DBConn { INSTANCE; ... }
Consequences — When to Use / When NOT to Use Singleton
Aspect Details
Use When Exactly one object is needed to coordinate system-wide actions (logger, config,
DB pool, cache, registry)
Avoid When You think you only need one instance today but may need more later (violates
flexibility); in unit-testable code
Aspect Details
Pros Controlled access to sole instance; global access; lazy initialization saves
memory until needed
Cons Global state makes testing hard; hides dependencies (functions that use it
secretly depend on it); violates SRP (manages own lifecycle AND does its job)
4.2 Factory Method Pattern
📖 Factory Method Pattern
INTENT: Define an interface for creating an object, but let SUBCLASSES decide which class
to instantiate. Factory Method lets a class defer instantiation to subclasses.
PROBLEM: A class needs to create objects but shouldn't know (or depend on) the exact
class of object to create.
SOLUTION: Define a factory method in a creator class/interface. Subclasses override the
factory method to return different product types.
Also called: 'Virtual Constructor'
CATEGORY: Creational
Structure and Participants
Participant Role
Product Defines the interface for objects the factory method creates
(interface/abstract)
ConcreteProduct Implements the Product interface — the actual object to be created
Creator (abstract class) Declares the factory method that returns a Product. May call factory
method to create products.
ConcreteCreator Overrides the factory method to return an instance of ConcreteProduct
📊 Factory Method Class Diagram
+====================+ +====================+
| <<interface>> | | <<abstract>> |
| Notification | | NotificationSender|
|--------------------| |--------------------|
| + send(msg: String)| | + sendAlert(msg) | uses factory
method
+====================+ | + createNotif(): | <- FACTORY METHOD
A | Notification | (abstract)
______|______ +====================+
| | A
| | _________|_________
+=======+ +==========+ | |
| Email | | SMS | +=============+ +===============+
| Notif | | Notif | |EmailSender | |SMSSender |
+=======+ +==========+ |-------------| |---------------|
ConcreteProducts |+createNotif()| |+createNotif() |
| return new | | return new |
| EmailNotif()| | SMSNotif() |
+=============+ +===============+
ConcreteCreators
💡 Factory Method — Document Editor
Scenario: A document editor that can create different types of documents (PDF, Word,
HTML).
// Product interface
interface Document {
void open();
void save();
void close();
}
// ConcreteProducts
class PDFDocument implements Document { void open(){...} void save(){...} void close(){...} }
class WordDocument implements Document { void open(){...} void save(){...} void close(){...}
}
class HTMLDocument implements Document { void open(){...} void save(){...} void close(){...}
}
// Creator — abstract class with factory method
abstract class DocumentEditor {
abstract Document createDocument(); // FACTORY METHOD — subclasses override
void newDocument() {
Document doc = createDocument(); // calls the factory method
[Link]();
}
}
// ConcreteCreators — each decides which product to create
class PDFEditor extends DocumentEditor { Document createDocument() { return new
PDFDocument(); } }
class WordEditor extends DocumentEditor { Document createDocument() { return new
WordDocument(); } }
class HTMLEditor extends DocumentEditor { Document createDocument() { return new
HTMLDocument(); } }
// Client
DocumentEditor editor = new PDFEditor();
[Link](); // creates a PDFDocument — client doesn't know/care which type
Pattern Factory Method vs Singleton
Purpose Factory: creates different types of objects. Singleton: ensures only one
instance.
# Instances Factory: creates multiple instances (one per call). Singleton: exactly one.
Polymorphism Factory: subclasses decide what to create. Singleton: no subclassing
needed.
When to use Factory: when creation logic needs to vary. Singleton: when exactly one
instance needed.
5: STRUCTURAL PATTERNS
5.1 Adapter Pattern
📖 Adapter Pattern
INTENT: Convert the interface of a class into another interface that clients expect. Adapter
lets classes work together that couldn't otherwise because of incompatible interfaces.
PROBLEM: You want to use an existing class (Adaptee) but its interface doesn't match what
your client expects.
SOLUTION: Create an Adapter class that wraps the Adaptee and translates calls from the
client's expected interface to the Adaptee's actual interface.
Also called: Wrapper
CATEGORY: Structural
Real-world analogy: A power adapter lets you plug a US device into a UK socket — it adapts
the interface without changing either the device or the socket.
Structure and Participants
Participant Role
Target (interface) Defines the domain-specific interface that the Client uses
Client Collaborates with objects conforming to the Target interface
Adaptee Defines an existing interface that needs adapting (the 'incompatible' class)
Adapter Implements the Target interface; wraps the Adaptee; translates Target
calls to Adaptee calls
📊 Adapter Class Diagram
+==============+ uses +==============+ wraps
+=============+
| Client |------------>| <<interface>> | | Adaptee
|
+==============+ | Target | |-------------
|
|---------------| |
+oldMethod()|
| +request() |
+=============+
+==============+ A
A |
+==============+ +==============+
| Adapter |-------->| Adaptee |
|--------------| calls | |
| +request() | | +oldMethod()|
| { adaptee | +=============+
| .oldMethod()}|
+==============+
💡 Adapter Pattern — Payment Gateway Integration
Scenario: Your e-commerce system uses IPaymentGateway interface.
A new third-party payment library (PayPalSDK) has a different interface.
// Target interface — what your system expects
interface IPaymentGateway {
boolean charge(String customerId, double amount);
boolean refund(String transactionId);
}
// Adaptee — existing PayPal SDK with different interface (cannot change it)
class PayPalSDK {
void makePayment(String email, double totalAmount) { ... }
void reversePayment(String paypalTransactionRef) { ... }
}
// Adapter — bridges the incompatibility
class PayPalAdapter implements IPaymentGateway {
private PayPalSDK paypal = new PayPalSDK();
public boolean charge(String customerId, double amount) {
[Link](customerId + '@[Link]', amount); // translate
return true;
}
public boolean refund(String transactionId) {
[Link](transactionId); // translate
return true;
}
}
// Client code — unchanged, works with any IPaymentGateway
IPaymentGateway gateway = new PayPalAdapter();
[Link]('customer123', 499.0); // transparently uses PayPal under the hood
Real-world Adapters: JDBC drivers (adapts different DB APIs to common JDBC interface),
InputStreamReader (adapts byte stream to character stream), legacy system integrations
5.2 Facade Pattern
📖 Facade Pattern
INTENT: Provide a SIMPLIFIED UNIFIED INTERFACE to a complex subsystem. Facade
defines a higher-level interface that makes the subsystem easier to use.
PROBLEM: A subsystem has many complex classes with complex interdependencies.
Clients shouldn't need to understand all the complexity to use it.
SOLUTION: Introduce a Facade class that provides a simple interface to the complex
subsystem. The Facade delegates client requests to the appropriate subsystem classes.
CATEGORY: Structural
Real-world analogy: A hotel concierge is a Facade — you ask the concierge for anything;
they coordinate with the kitchen, housekeeping, transport, etc. You don't deal with each
department directly.
📊 Facade Class Diagram
Client
|
| simple calls
v
+========================+
| Facade | <-- simple, unified interface
|------------------------|
| + placeOrder() |
| + trackShipment() |
| + processReturn() |
+========================+
| | |
| | |
v v v
+---------+ +-------+ +-----------+
|Inventory| |Payment| |Shipping | <-- complex subsystem
|Service | |Service| |Service | classes
+---------+ +-------+ +-----------+
| |
+---------+ +-----------+
|Product | |Courier |
|Catalog | |Integration|
+---------+ +-----------+
Client only knows Facade. Facade knows all subsystem classes.
Subsystem classes don't know about Facade (Facade depends on them, not
vice versa).
💡 Facade Pattern — Home Theater System
COMPLEX SUBSYSTEM — 6 classes to control a home theater:
Amplifier, Tuner, DvdPlayer, CdPlayer, Projector, TheaterLights
WITHOUT Facade — client must know all 6 classes:
[Link](10);
[Link]();
[Link]();
[Link]();
[Link](dvd);
[Link]();
[Link](5);
[Link]();
[Link](movie);
// 9 lines, 5 different objects — must know entire subsystem!
WITH Facade — client calls ONE method:
class HomeTheaterFacade {
private Amplifier amp; private DvdPlayer dvd;
private Projector projector; private TheaterLights lights;
void watchMovie(String movie) { // FACADE METHOD
[Link](10);
[Link]();
[Link]();
[Link](); [Link](dvd); [Link](); [Link](5);
[Link](); [Link](movie);
}
void endMovie() { [Link](); [Link](); [Link](); [Link](); }
}
// Client code — simple!
HomeTheaterFacade theater = new HomeTheaterFacade(amp, dvd, proj, lights);
[Link]('Inception'); // one call does everything
[Link]();
Real-world Facades: SLF4J Logger (facades Log4j, Logback, etc.),
JDBC (facades database driver details), Spring JdbcTemplate, AWS SDK clients
Aspect Adapter Pattern Facade Pattern
Intent Convert incompatible interface to Simplify a complex subsystem with
compatible one unified interface
Problem it solves Incompatible interfaces between Too much complexity exposed to
existing classes clients
What it wraps ONE class (the Adaptee) A whole SUBSYSTEM (multiple
classes)
Creates new YES — the Target interface YES — the simplified Facade interface
interface?
Changes NO — adapts without changing NO — Facade delegates to unchanged
underlying code? Adaptee subsystem
Analogy Power plug adapter (makes Hotel concierge (hides complex hotel
incompatible things fit) operations)
6: BEHAVIORAL PATTERNS
6.1 Strategy Pattern
📖 Strategy Pattern
INTENT: Define a FAMILY OF ALGORITHMS, encapsulate each one, and make them
interchangeable. Strategy lets the algorithm vary independently from clients that use it.
PROBLEM: A class needs to perform an operation, but the specific algorithm for it may vary.
Hard-coding the algorithm in the class makes it inflexible.
SOLUTION: Extract the algorithm into its own class (Strategy). The context class holds a
reference to a Strategy interface. Different concrete strategies implement different
algorithms.
CATEGORY: Behavioral
Key Principle: Identify the aspects that vary and separate them from what stays the same.
Structure and Participants
Participant Role
Strategy (interface) Declares the interface common to all supported algorithms
ConcreteStrategy Implements one specific algorithm using the Strategy interface
Context Maintains a reference to a Strategy object; configured with a
ConcreteStrategy; may let clients set/change the strategy
📊 Strategy Class Diagram
+====================+ +====================+
| Context |-------->| <<interface>> |
|--------------------| uses | ISortStrategy |
| - strategy: | |--------------------|
| ISortStrategy | | + sort(data[]):void|
|--------------------| +====================+
| + setStrategy(s) | A
| + executeSort() | ___________|___________
+====================+ | | |
+==========+ +=========+ +=========+
|BubbleSort| |QuickSort| |MergeSort|
|----------| |---------| |---------|
|+sort() | |+sort() | |+sort() |
+==========+ +=========+ +=========+
ConcreteStrategies — interchangeable algorithms
💡 Strategy Pattern — Sorting and Discount Systems
EXAMPLE 1: Sorting Strategy
interface ISortStrategy { void sort(int[] data); }
class BubbleSortStrategy implements ISortStrategy { void sort(int[] d) { /*bubble*/ } }
class QuickSortStrategy implements ISortStrategy { void sort(int[] d) { /*quick*/ } }
class MergeSortStrategy implements ISortStrategy { void sort(int[] d) { /*merge*/ } }
class DataProcessor {
private ISortStrategy strategy;
void setStrategy(ISortStrategy s) { [Link] = s; }
void process(int[] data) { [Link](data); } // delegates to strategy
}
DataProcessor dp = new DataProcessor();
[Link](new QuickSortStrategy()); // choose algorithm at runtime
[Link](data);
[Link](new MergeSortStrategy()); // switch algorithm at runtime!
[Link](bigData);
EXAMPLE 2: Discount Strategy — E-Commerce
interface IDiscountStrategy { double applyDiscount(double price); }
class NoDiscount implements IDiscountStrategy { double applyDiscount(double p) {
return p; } }
class PercentDiscount implements IDiscountStrategy { double applyDiscount(double p) {
return p * 0.8; } }
class FlatDiscount implements IDiscountStrategy { double applyDiscount(double p) {
return p - 100; } }
class SeasonalDiscount implements IDiscountStrategy { double applyDiscount(double p) {
return p * 0.5; } }
class ShoppingCart {
private IDiscountStrategy discountStrategy = new NoDiscount();
void applyPromotion(IDiscountStrategy s) { [Link] = s; }
double checkout() { return [Link](subtotal); }
}
[Link](new SeasonalDiscount()); // Diwali sale — 50% off!
[Link](new PercentDiscount()); // Regular member — 20% off
📝 Strategy vs Polymorphism (GRASP)
Strategy uses polymorphism but is more flexible because:
- GRASP Polymorphism: behavior varies by TYPE of object (subclass overrides method)
- Strategy: behavior can be CHANGED AT RUNTIME by swapping strategy objects
- Strategy supports multiple independent algorithms for the SAME object
- Strategy allows combining algorithms (e.g., QuickSort for small data, MergeSort for large)
6.2 State Pattern
📖 State Pattern
INTENT: Allow an object to ALTER ITS BEHAVIOR when its internal state changes. The
object will appear to change its class.
PROBLEM: An object's behavior depends on its current state, and the object must change its
behavior at runtime depending on that state. Hard-coding if/switch on state makes the code
inflexible and hard to extend.
SOLUTION: Represent each state as a separate State class. The Context object delegates
behavior to the current State object.
CATEGORY: Behavioral
Relationship with State Diagram: The State pattern is the direct implementation of a UML
State Diagram in code.
Structure and Participants
Participant Role
Context Defines the interface of interest to clients; maintains a reference to the
current State object; delegates state-specific requests to the current State
State (interface/abstract) Defines an interface for encapsulating the behavior associated with a
particular state of the Context
ConcreteState Each subclass implements behavior associated with a state of the
Context. Handles requests and may transition the context to a new state.
📊 State Pattern Class Diagram
+====================+ +====================+
| Context |-------->| <<interface>> |
|--------------------| uses | IOrderState |
| - state: IOrderSt | |--------------------|
| - orderId: String | | + confirm(ctx) |
|--------------------| | + ship(ctx) |
| + setState(s) | | + cancel(ctx) |
| + confirm() | | + deliver(ctx) |
| + ship() | +====================+
| + cancel() | A
+====================+ ______________|______________
| | | |
+========+ +======+ +========+ +=========+
|Pending | |Confirm| |Shipped| |Delivered|
|State | |edState| |State | |State |
+========+ +======+ +========+ +=========+
ConcreteStates — each handles events differently
💡 State Pattern — Order Lifecycle
// State interface
interface IOrderState {
void confirm(OrderContext ctx);
void ship(OrderContext ctx);
void cancel(OrderContext ctx);
void deliver(OrderContext ctx);
}
// ConcreteState: PendingState
class PendingState implements IOrderState {
public void confirm(OrderContext ctx) {
[Link]('Order confirmed!');
[Link](new ConfirmedState()); // transition to new state
}
public void ship(OrderContext ctx) { [Link]('Cannot ship — not confirmed yet!');
}
public void cancel(OrderContext ctx) { [Link](new CancelledState()); }
public void deliver(OrderContext ctx) { [Link]('Invalid operation'); }
}
// ConcreteState: ConfirmedState
class ConfirmedState implements IOrderState {
public void confirm(OrderContext ctx) { [Link]('Already confirmed'); }
public void ship(OrderContext ctx) {
[Link]('Order shipped!');
[Link](new ShippedState());
}
public void cancel(OrderContext ctx) { [Link](new CancelledState()); }
public void deliver(OrderContext ctx) { [Link]('Not yet shipped'); }
}
// Context
class OrderContext {
private IOrderState state = new PendingState(); // initial state
public void setState(IOrderState s) { [Link] = s; }
public void confirm() { [Link](this); } // delegates to current state
public void ship() { [Link](this); }
public void cancel() { [Link](this); }
}
// Client
OrderContext order = new OrderContext();
[Link](); // -> 'Order confirmed!' (state changes to ConfirmedState)
[Link](); // -> 'Order shipped!' (state changes to ShippedState)
[Link](); // -> 'Cannot cancel — order already shipped!'
Aspect Strategy Pattern State Pattern
Intent Allow algorithm to be swapped by the Allow object behavior to change as
client state changes
Who changes the Client — explicitly sets a different Context or State itself — transitions
behavior? strategy automatically
States aware of each Strategies are independent States often know about other states
other? (for transitions)
Use when Multiple interchangeable algorithms Object has distinct states with
for same operation different behaviors
Runtime behavior Client chooses algorithm; can switch Object transitions through states
anytime based on events
Analogy Choosing different sort algorithms for Vending machine or traffic light
same data changing behavior by state
SUMMARY TABLES:
Table 1: All 9 GRASP Patterns
Pattern Question Answered Key Rule Example
Creator Who creates B? A creates B if A Order creates OrderItem
contains/records/uses/has
data for B
Information Expert Who does X? Assign to class with Cart calculates total price
needed data
Low Coupling How to minimize Minimize and stabilize Depend on interface, not
change impact? dependencies concrete class
High Cohesion How to keep classes One class = one focused SalaryCalculator only
focused? responsibility calculates salary
Controller Who handles system Dedicated facade or use- CheckoutController
events? case controller handles checkout events
Polymorphism How to handle type Override method per type, [Link]()
variations? avoid if/switch overridden in each
subtype
Pure Fabrication No domain class fits? Invent helper/service class OrderRepository,
EmailService
Indirection How to avoid direct Add mediator/intermediary TaxService between
coupling? object Invoice and TaxAPI
Protected How to shield from Wrap variation behind IPaymentGateway
Variations change? stable interface protects from payment
tech changes
Table 2: GOF Design Patterns — (only 4 Patterns in Syllabus)
Pattern Category Intent Key Participants Use When
Singleton Creational Ensure exactly one Singleton class with Logger, DB
instance with global private constructor pool, Config
access and static manager,
getInstance() Thread pool
Factory Creational Let subclasses decide Creator, Object type
Method which class to ConcreteCreator, determined at
instantiate Product, runtime;
ConcreteProduct subclasses
create
specialized
objects
Adapter Structural Convert incompatible Target (interface), Integrating
interface to expected Adapter, Adaptee, legacy/third-
one Client party code with
different
interfaces
Pattern Category Intent Key Participants Use When
Facade Structural Simplify complex Facade, Subsystem Reduce
subsystem with unified classes, Client complexity for
interface clients of
complex
subsystems
Strategy Behavioral Define family of Strategy interface, Multiple
algorithms, make them ConcreteStrategies, algorithms for
interchangeable Context same
operation;
switch
algorithm at
runtime
State Behavioral Change object behavior State interface, Object
as internal state ConcreteStates, behavior varies
changes Context significantly
based on
state; state
machine
implementation
Table 3: Creational vs Structural vs Behavioral
Category Focus Patterns Question
Answered
Creational HOW objects are Singleton, Factory Method, How should this
created; hide creation Abstract Factory, Builder, object be created?
complexity Prototype
Structural HOW classes/objects are Adapter, Facade, Bridge, How should these
COMPOSED into larger Composite, Decorator, classes/objects fit
structures Flyweight, Proxy together?
Behavioral HOW objects Strategy, State, Observer, Who is responsible
COMMUNICATE and Command, Iterator, Mediator, for this? How do
SHARE responsibilities Template Method, Visitor... objects talk?
Table 4: Selecting the Right Pattern — Decision Guide
If you need to... Use this Pattern Reason
Ensure only one object of a Singleton Controls instantiation; global access
class exists globally point
Let subclasses choose what Factory Method Defers instantiation to subclasses
type of object to create
Use a class that has the wrong Adapter Wraps and translates the incompatible
interface interface
Simplify a complex set of Facade Provides simple unified interface to
classes for clients subsystem
If you need to... Use this Pattern Reason
Switch between different Strategy Encapsulates algorithms;
algorithms at runtime interchangeable
Change object behavior based State Each state handles events differently
on its state
Avoid if/switch chains for Polymorphism (GRASP) Polymorphic dispatch replaces
different types or State/Strategy conditional logic
Decouple classes that must Indirection (GRASP) or Intermediary prevents direct
communicate Adapter/Facade dependency
Protect code from future Protected Variations Stable interface absorbs variations
changes in one area (GRASP) + interfaces
Assign creation responsibility Creator (GRASP) Owner/container creates what it contains
to the natural owner
GRASP Principles
• Creator: Assign creation to the class that aggregates, records, uses, or has data for the created
object.
• Information Expert: The most fundamental principle — assign responsibility to the class that
has the required information.
• Low Coupling: Minimize dependencies between classes; prefer interfaces over concrete
classes; changes stay local.
• High Cohesion: Keep each class focused on one responsibility; avoid 'God classes' that do
everything.
• Controller: Delegate UI events to a dedicated controller class (Facade or Use-Case controller).
Controllers delegate, not do.
• Polymorphism: Replace if/switch type checks with polymorphic method dispatch; each type
handles its own behavior.
• Pure Fabrication: Invent non-domain helper classes (Repository, Service) when no domain
class is a good fit.
• Indirection: Add an intermediary to decouple two classes that must communicate.
• Protected Variations: Wrap variation points behind stable interfaces; shield clients from
implementation changes.
Useful LINK: [Link]
GOF Design Patterns
• Singleton (Creational): One instance, global access. Use for Logger, DB pool, Config. Thread
safety needs special care.
• Factory Method (Creational): Subclasses decide what to create. Decouples client from
concrete product classes.
• Adapter (Structural): Wraps one class to fix incompatible interface. The 'plug adapter' of
software — makes things fit together.
• Facade (Structural): Simplifies complex subsystems. One simple interface hides all the
complexity beneath.
• Strategy (Behavioral): Encapsulates a family of algorithms. Client can switch algorithms at
runtime without changing Context.
• State (Behavioral): Each state is a class. Context delegates to current state. Eliminates
if/switch state checks; directly implements State Diagrams.
Some Questions to avoid confusion:
Question Answer
What is the difference between GRASP: principles for ASSIGNING RESPONSIBILITIES to
GRASP and GOF patterns? classes (foundational). GOF: specific STRUCTURAL
TEMPLATES with participants and collaborations
(implementational).
When does Singleton cause In unit testing (global state is hard to mock); when you later
problems? need multiple instances; when it hides dependencies.
What is the core difference between Adapter wraps ONE class to fix incompatible interface. Facade
Adapter and Facade? wraps a whole SUBSYSTEM to simplify it.
What is the core difference between Strategy: client swaps algorithms consciously. State: object's
Strategy and State? behavior changes automatically as state transitions occur
internally.
How does Factory Method support Client depends on Product interface, not ConcreteProduct.
Low Coupling? ConcreteCreator creates ConcreteProduct — client never
references concrete classes.
How does Adapter relate to Both protect clients from variation. Protected Variations is the
Protected Variations (GRASP)? principle; Adapter is one concrete pattern that implements it.
What is a Pure Fabrication and give A class with no real-world counterpart, invented purely for
an example? design reasons. Examples: Repository, Service, Logger,
Factory, Helper.