โ Back to OOP Concepts
1๏ธโฃ
Principles
Single Responsibility
A class should have only one reason to change โ it should do one thing and do it well.
The Problem
Classes that handle multiple responsibilities (e.g., business logic, persistence, and formatting all in one) become fragile: a tweak to one responsibility risks breaking another, and the class becomes a magnet for further unrelated changes.
How It Works
- โบEach class or module encapsulates a single piece of functionality
- โบResponsibilities are split into separate classes, each with a focused purpose
- โบImproves testability โ smaller classes with fewer dependencies are easier to test in isolation
- โบ"Reason to change" is the key heuristic, not "number of methods"
When to Use
โA class mixes data access, business rules, and presentation logic
โRefactoring "god classes" that have grown to do everything
โDesigning new classes โ ask "what is this class's one job?"
Real-World Examples
๐
iOS
Separating networking (APIClient), persistence (Storage), and presentation (ViewModel)
๐ค
Android
Splitting a fat Activity into ViewModel, Repository, and UI layers
๐
Web
Separating route handlers, business/service logic, and data-access layers
Code Examples
Swift
// Each type has exactly one responsibility
struct Invoice {
let id: String
let amount: Double
}
struct InvoiceCalculator {
func total(for invoice: Invoice, taxRate: Double) -> Double {
invoice.amount * (1 + taxRate)
}
}
struct InvoicePrinter {
func format(_ invoice: Invoice) -> String {
"Invoice #\(invoice.id): $\(invoice.amount)"
}
}
struct InvoiceRepository {
func save(_ invoice: Invoice) {
// persist to database
print("Saved invoice \(invoice.id)")
}
}