Принцип відкритості/закритості

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

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

« Дослідження показали, що всі найкращі проектувальники можуть передбачати зміни.[1]  »

Тому при проектуванні системи необхідно закласти в неї можливість безболісного додавання функціоналу чи його зміни.

Історія[ред.ред. код]

Історично склалося, що термін «принцип відкритості/закритості» має два значення. Обидва використовують успадкування для вирішення дилеми, проте вони різняться цілями, способами та результатами.

Принцип відкритості/закритості Мейєра[ред.ред. код]

Бертран Мейєр відомий як основоположник терміну «принцип відкритості/закритості», що з'явився в 1988 році в його книзі «Object-Oriented Software Construction». Ідея полягала в тому, що розроблена реалізація класу в подальшому вимагає лише виправлення помилок, а нові чи змінені функції вимагають створення нового класу. Він може використовувати код предка завдяки механізму успадкування, але не обов'язково слідує його інтерфейсу. Очевидно, що визначення Мейєра підтримує ідею «успадкування реалізації».

Поліморфний принцип відкритості/закритості[ред.ред. код]

В 1990-х принцип відкритості/закритості став де-факто перевизначений для застосування з абстрактними інтерфейсами, реалізації яких можуть бути як змінені, так і поліморфно підмінені.

На відміну від ідей Мейєра, це визначення підтримує ідею «Успадкування від абстрактних класів». Специфікації інтерфейсів можуть бути успадкувані, проте реалізація має лишатися незмінною. Існуючий інтерфейс має бути закритий для модифікацій, а нові реалізації мусять його реалізовувати. Однією з публікацій, що популяризувала такий підхід, була стаття Роберта Мартіна «The Open-Closed Principle»[2].

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

Нехай в системі є клас спортивного парсера для створення різних об'єктів SportInfo, що інкапсулюють різну інформацію про гру:

public class BadParserRealization {
   public SportInfo parse(String[] data) {
      SportInfo result;
      if (data[0].equals("nba")) {
         // code to parse NBA
      } else {
         if (data[0].equals("nhl")) {
            // code to parse NHL
         }
      }
      return result;
   }
}

На перший погляд все нормально, проте при спробі додати нову лігу, наприклад MLB, виникнуть проблеми. Програмісти будуть змушені редагувати parse(String[] data), і, можливо, зачеплять робочий код.

Дана проблема легко вирішується застосуванням шаблону проектування Builder. Рішення може бути наступним:

public class BetterParserRealization {
   private Map<String, SportInfoBuilder> builders;
 
   public BetterParserRealization(Map<String, SportInfoBuilder> builders) {
         setBuilders(builders);
   }
 
   public void setBuilders(Map<String, SportInfoBuilder> builders) {
         if (builders == null) {
            throw new NullPointerException("Builders can not be null!");
         }
         this.builders = builders;
   }
 
   public void parse(String[] data) {
      SportInfoBuilder builder = builders.get(data[0]);
      builder.build(data);
      return builder.getResult();
   }
}
public interface SportInfoBuilder {
   public void build(String[] data);
}
public class NBASportInfoBuilder implements SportInfoBuilder {
   private SportInfo result;
 
   @Override
   public void build(String[] data) {
      // code to parse NBA
   }
 
   public SportInfo getResult() {
      return result;
   }
}

Спочатку визначається парсер, що може обробити вхідний масив. Відношення між ним та лігою встановлюється іншим класом (це може бути шаблон проектування Abstract Factory або IoC контейнер). Варто відмітити, що при додаванні нової ліги, потреби в редагуванні вже робочого коду відпадають, натомість необхідно створити новий клас, що буде реалізовувати інтерфейс SportInfoBuilder.

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

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

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

SOLID — буква «O» означає принцип відкритості/закритості (англ. Open Closed Principle).

OCP та SRP[ред.ред. код]

OCP та SRP дуже близько пов'язані. Справді, адже чим більше було покладено на плечі класу, тим більше шансів на те, що його доведеться змінювати. Тому, порушуючи SRP, програміст скоріше за все, порушує і OCP.

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

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