Принцип розділення інтерфейсу

Матеріал з Вікіпедії — вільної енциклопедії.
Перейти до навігації Перейти до пошуку

Принцип розділення інтерфейсу (англ. Interface Segregation Principle, ISP) — важливий принцип об'єктно-орієнтованого програмування, визначений Робертом Мартіном у такому вигляді:

Клієнти не повинні залежати від методів, які вони не використовують. [1]

Оригінальний текст (англ.)
The interface-segregation principle (ISP) states that no client should be forced to depend on methods it does not use.

Отже, даний принцип означає, що занадто «товсті» інтерфейси необхідно розділяти на менші та специфічні, щоб їх клієнти знали лише про ті методи, що необхідні для них у роботі. Як результат, при зміні певного функціоналу, незмінними мають лишитися ті класи, що не використовують його. Тобто виконання цього принципу допомагає системі залишатися гнучкою при внесенні до неї змін та лишатися простою для рефакторингу.

Приклад[ред. | ред. код]

Приклад порушення ISP[ред. | ред. код]

Для прикладу порушення уявімо собі таку систему:

public interface Animal {
	void eat();
	void fly();
	void bark();
}
public class Bird implements Animal {
	@Override
	public void eat() {
		// деяка реалізація
	}
	
	@Override
	public void fly() {
		// деяка реалізація
	}
	
	@Override
	public void bark() {
		throw new UnsupportedOperationException();
	}
}
public class Dog implements Animal {
	@Override
	public void eat() {
		// деяка реалізація
	}
	
	@Override
	public void fly() {
		throw new UnsupportedOperationException();
	}
	
	@Override
	public void bark() {
		// деяка реалізація
	}
}

Що буде, коли в систему доведеться додавати новий клас, наприклад Cat? Легко здогадатися, якою буде його реалізація.

Застосування ISP[ред. | ред. код]

Виходом з цієї ситуації є використання розділення інтерфейсу. Так званий «товстий» Animal варто розділити на наступні два інтерфейси: Flyable та Barkable.

public interface Animal {
	void eat();
}
public interface Flyable {
	void fly();
}
public interface Barkable {
	void bark();
}
public class Bird implements Animal, Flyable {
	@Override
	public void eat() {
		// деяка реалізація
	}
	
	@Override
	public void fly() {
		// деяка реалізація
	}
}
public class Dog implements Animal, Barkable {
	@Override
	public void eat() {
		// деяка реалізація
	}
	
	@Override
	public void bark() {
		// деяка реалізація
	}
}

Після застосування такого підходу класи стають «здоровими», з них зникають зайві методи.

ISP та шаблони проектування[ред. | ред. код]

Проте інколи складається ситуація, що не дозволяє зробити такий рефакторинг. Тоді варто застосовувати архітектурне рішення, що надає паттерн Адаптер. Система набуває такого вигляду:

public interface Animal {
	void eat();
	void fly();
	void bark();
}
public interface Eatable {
	void eat();
}
public interface Flyable {
	void fly();
}
public interface Barkable {
	void bark();
}
public class FlyingAdapter implements Eatable, Flyable {
	private Animal animal;
	
	public FlyingAdapter(Animal animal) {
		this.animal = animal;
	}
	
	@Override
	public void fly() {
		animal.fly();
	}
	
	@Override
	public void eat() {
		animal.eat();
	}
}
public class BarkingAdapter implements Eatable, Barkable {
	private Animal animal;
	
	public BarkingAdapter(Animal animal) {
		this.animal = animal;
	}
	
	@Override
	public void bark() {
		animal.bark();
	}
	
	@Override
	public void eat() {
		animal.eat();
	}
}

Наслідки використання цього підходу наступні:

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

Переваги та недоліки[ред. | ред. код]

Серед плюсів варто відзначити наступні:

  • при необхідності створення нової реалізації інтерфейсу немає потреби реалізовувати непотрібні методи;
  • клієнтський код отримує лише те, що потрібне для його роботи.

Мінус використання полягає в зростанні кількості інтерфейсів, що приводить до зростання складності системи.

Використання[ред. | ред. код]

SOLID — буква «I» означає принцип розділення інтерфейсу (англ. Interface Segregation Principle).

Примітки[ред. | ред. код]

  1. Martin, Robert. The Interface Segregation Principle (PS). Архів (PDF) оригіналу за 31 серпня 2012. Процитовано 5 жовтня 2006.

Посилання[ред. | ред. код]