DevPrep

All topics › Object-oriented programming (OOP)

Single Responsibility Principle (SRP)

Junior

Essence

SRP is the first letter of SOLID: a module should have one reason to change. Robert Martin refines it: a "reason" is one interested actor (a team, a department, a role). If a type is edited for a new screen layout, for an API change and for a new storage format, it has three responsibilities.

What a violation looks like in iOS

The classic Massive View Controller: the controller hits the network, parses JSON, formats dates, configures cells and writes to UserDefaults. Any edit touches everything, and it can only be tested through the UI.

Example of splitting

import Foundation

struct Order: Codable { let id: Int; let total: Decimal }

// Violation: one type loads, formats and persists — three reasons to change.
// Better: three small types with one responsibility each.
protocol OrderRepository { func orders() async throws -> [Order] }

struct OrderFormatter {
    func title(for order: Order) -> String {
        "Order #\(order.id): \(order.total)"
    }
}

@MainActor @Observable
final class OrdersViewModel {
    private(set) var titles: [String] = []
    private let repository: any OrderRepository
    private let formatter = OrderFormatter()

    init(repository: any OrderRepository) { self.repository = repository }

    func load() async {
        let orders = (try? await repository.orders()) ?? []
        titles = orders.map(formatter.title(for:))
    }
}

OrderRepository is responsible for fetching data, OrderFormatter for presentation, OrdersViewModel for screen state. Each is tested separately and the repository is replaced with a stub.

What the interviewer looks for

Practice this before your interview

DevPrep turns these answers into one-minute challenges: what does this code print, find the bug, mock interviews. One challenge a day.

Get DevPrep on the App Store