Про принцип інтерфейсів (Interface Segregation Principle - ISP)

Принцип інтерфейсів (Interface Segregation Principle - ISP) є одним з п’яти принципів SOLID і стверджує, що клієнти не повинні залежати від інтерфейсів, які вони не використовують. Замість того, щоб визначати одне велике інтерфейс, котрий включає усі можливі методи, ISP підтримує ідею розділення цього інтерфейсу на менші, специфічні для клієнтів.

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

Розділення інтерфейсу - інтерфейси повинні бути невеликими та специфічними для клієнтів. Клієнти повинні залежати лише від тих інтерфейсів, які необхідні для їхньої роботи.
Мінімізація залежностей - клієнти не повинні отримувати методи, які для них не є необхідними. Збільшення кількості методів в інтерфейсі може призвести до зайвих або непотрібних залежностей.
Робота з інтерфейсами замість конкретних класів - клієнти повинні програмувати на основі інтерфейсів, а не конкретних класів. Це дозволяє легше замінювати реалізації інтерфейсів без впливу на клієнтський код.

В Ruby інтерфейси набагато менш формальні, оскільки мова не має строгого поняття інтерфейсу, як у Java або C#. Проте, в Ruby ми можемо використовувати модулі для створення схожого ефекту. Давайте розглянемо приклад:

# Поганий приклад з єдиним модулем
module Worker
  def work
    raise NotImplementedError, 'Subclasses must implement the work method'
  end

  def eat
    raise NotImplementedError, 'Subclasses must implement the eat method'
  end

  def sleep
    raise NotImplementedError, 'Subclasses must implement the sleep method'
  end
end

class Programmer
  include Worker

  def work
    puts "Programming..."
  end

  def eat
    puts "Eating lunch..."
  end

  def sleep
    puts "Sleeping at night..."
  end
end

class Manager
  include Worker

  def work
    puts "Managing projects..."
  end

  def eat
    puts "Eating lunch..."
  end

  def sleep
    puts "Sleeping at night..."
  end
end

В цьому прикладі модуль Worker включає методи work, eat і sleep, але не всі класи, які його включають, можуть використовувати всі ці методи. Це може порушити принцип інтерфейсів (ISP), оскільки класи, можливо, не використовують всі методи інтерфейсу.

Тепер давайте розділимо цей модуль на менші, специфічні для класів модулі:

module Workable
  def work
    raise NotImplementedError, 'Subclasses must implement the work method'
  end
end

module Eatable
  def eat
    raise NotImplementedError, 'Subclasses must implement the eat method'
  end
end

module Sleepable
  def sleep
    raise NotImplementedError, 'Subclasses must implement the sleep method'
  end
end

class Programmer
  include Workable
  include Eatable
  include Sleepable

  def work
    puts "Programming..."
  end

  def eat
    puts "Eating lunch..."
  end

  def sleep
    puts "Sleeping at night..."
  end
end

class Manager
  include Workable
  include Eatable
  include Sleepable

  def work
    puts "Managing projects..."
  end

  def eat
    puts "Eating lunch..."
  end

  def sleep
    puts "Sleeping at night..."
  end
end

Тепер класи включають лише ті модулі, які є для них важливими, виконуючи тим самим принцип інтерфейсів (ISP) - залежати лише від того, що необхідно.