DevPrep

All topics › Object-oriented programming (OOP)

Composition vs inheritance

Junior

Essence

Inheritance is an is-a relationship ("a button is a view"): the subclass gets all of the parent's behaviour. Composition is a has-a relationship ("a screen has a logger"): the object is assembled from parts and delegates work to them. The rule of thumb is "prefer composition over inheritance"; subclass when you really need to extend a framework (UIViewController) or the domain model is genuinely hierarchical.

Example

// Inheritance: "is-a". A deep hierarchy is rigid.
class BaseViewController {}
class LoggingViewController: BaseViewController {}

// Composition: "has-a". Behaviour is injected and replaceable.
protocol Logger { func log(_ message: String) }
struct ConsoleLogger: Logger { func log(_ message: String) { print(message) } }
struct SilentLogger: Logger { func log(_ message: String) {} }

final class ProfileViewModel {
    private let logger: any Logger
    init(logger: any Logger = ConsoleLogger()) { self.logger = logger }
    func load() { logger.log("loading profile") }
}

ProfileViewModel().load()                       // prints
ProfileViewModel(logger: SilentLogger()).load() // prints nothing

To add logging through inheritance you need a LoggingViewController, then a LoggingAnalyticsViewController... a combinatorial explosion. Composition lets you assemble any set of behaviours and swap them in tests.

Why composition is usually better

When inheritance is appropriate

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