SOLID Principles in 59 Seconds

O — Open/Closed Principle

2 of 5
The principle

Open for extension, but closed for modification.

“Open” means the design allows new behavior to be added. “Closed” means existing code does not need to be edited each time that happens. A stable interface can let new implementations supply that behavior while existing code keeps using the same interface.

In the shipping notification example, NotificationDispatcher chooses a sender for each update. Its original send method checks the channel itself, so adding push notifications requires another branch.

Language

Original code

class NotificationDispatcher {
  send(message: string, channel: string) {
    if (channel === 'email') {
      new EmailSender().send(message)
    } else if (channel === 'sms') {
      new SmsSender().send(message)
    } else {
      throw new Error('Unknown channel: ' + channel)
    }
  }
}

Revised code

interface Sender {
  send(message: string): void
}

class EmailSender implements Sender {
  send(message: string) { emailApi.send(message) }
}

class SmsSender implements Sender {
  send(message: string) { smsApi.send(message) }
}

class PushNotificationSender implements Sender {
  send(message: string) { pushApi.send(message) }
}

class NotificationDispatcher {
  constructor(private senders: ReadonlyMap<string, Sender>) {}

  send(message: string, channel: string) {
    const sender = this.senders.get(channel)
    if (!sender) throw new Error('Unknown channel: ' + channel)
    sender.send(message)
  }
}

const senders = new Map<string, Sender>([
  ['email', new EmailSender()],
  ['sms', new SmsSender()],
  ['push', new PushNotificationSender()],
])

const dispatcher = new NotificationDispatcher(senders)