Про принцип відкритості/закритості (Open/Closed Principle - OCP)

Принцип відкритості/закритості (Open/Closed Principle - OCP) є одним з п’яти принципів SOLID і визначає, що класи повинні бути відкритими для розширення, але закритими для модифікацій. Це означає, що поведінка класу може бути розширена без зміни його вихідного коду.

Основні ідеї OCP.

  1. Відкритість для розширення - клас повинен надавати можливості для розширення свого поведінки. Це може бути досягнуто за допомогою інтерфейсів, абстракцій або використання паттернів проектування.
  2. Закритість для модифікацій - при цьому принципі клас повинен залишатися стійким до змін у своєму внутрішньому коді, тобто він закритий для модифікацій. Зміни повинні вноситися лише для введення нового функціоналу.
  3. Розширення за допомогою підкласів або інтерфейсів - новий функціонал додається шляхом створення нових класів (підкласів або об’єктів, що реалізують інтерфейси), які розширюють базовий клас, не модифікуючи його.

В Ruby принцип відкритості/закритості (OCP) також можна продемонструвати за допомогою інтерфейсів або поліморфізму. Ось приклад:

# Закритий для модифікацій клас
class Circle
  attr_reader :radius

  def initialize(radius)
    @radius = radius
  end

  def area
    Math::PI * radius**2
  end
end

# Відкритий для розширення інтерфейс
module Shape
  def area
    raise NotImplementedError, 'Subclasses must implement the area method'
  end
end

# Розширення нового функціоналу через новий клас
class ColoredCircle < Circle
  attr_reader :color

  def initialize(radius, color)
    super(radius)
    @color = color
  end

  def area
    # Використовуємо функціонал базового класу Circle
    super
  end
end

# Використання розширеного функціоналу
circle = ColoredCircle.new(5, 'red')
puts "Colored Circle Area: #{circle.area}"  # Використовує функціонал базового класу
puts "Color: #{circle.color}"

У цьому прикладі клас Circle є закритим для модифікацій, і його функціонал розширюється за допомогою нового класу ColoredCircle . ColoredCircle успадковує Circle і реалізує інтерфейс Shape , в якому обов’язково повинен бути метод area . Таким чином, принцип відкритості/закритості дотримується: ми можемо розширити функціонал, не модифікуючи вихідний код Circle .