โ† Back to OOP Concepts
๐Ÿ”“
Principles

Open/Closed

Software entities should be open for extension but closed for modification โ€” add new behavior without changing existing, tested code.

The Problem

When new requirements are handled by editing existing classes โ€” e.g., adding another `if` branch for each new type โ€” every change risks breaking existing functionality and requires re-testing the whole class.

How It Works

  • โ€บNew behavior is added by creating new classes/implementations, not by editing existing ones
  • โ€บAchieved through abstraction โ€” interfaces or protocols that existing code depends on
  • โ€บExisting, tested code remains untouched and stable
  • โ€บOften realized via the Strategy pattern, polymorphism, or plugin architectures

When to Use

โœ“Systems that frequently need new variants (payment methods, file exporters, discount rules)
โœ“Avoiding long if/else or switch chains that grow with each new type
โœ“Designing plugin-based architectures

Real-World Examples

๐ŸŽ
iOS

Adding a new UIView subclass without modifying UIKit itself

๐Ÿค–
Android

Adding a new PaymentMethod implementation without touching the checkout flow

๐ŸŒ
Web

Adding a new export format by implementing an Exporter interface

Code Examples

Swift
protocol DiscountStrategy {
    func apply(to price: Double) -> Double
}

struct NoDiscount: DiscountStrategy {
    func apply(to price: Double) -> Double { price }
}

struct PercentageDiscount: DiscountStrategy {
    let percent: Double
    func apply(to price: Double) -> Double { price * (1 - percent / 100) }
}

// New discount types are added without modifying PriceCalculator
struct PriceCalculator {
    let strategy: DiscountStrategy
    func finalPrice(for price: Double) -> Double {
        strategy.apply(to: price)
    }
}

let calculator = PriceCalculator(strategy: PercentageDiscount(percent: 20))
print(calculator.finalPrice(for: 100))  // 80.0