Принцип відкритості/закритості
Принцип відкритості/закритості (англ. Open Closed Principle, OCP) — важливий принцип об'єктно-орієнтованого програмування, який означає, що «програмні сутності, такі як класи, модулі, функції, методи та ін. мають бути відкритими для розширення та закритими для змін». Це означає, що вони можуть надавати можливість змінювати свою поведінку без або з мінімальними змінами коду.
Дослідження показали, що всі найкращі проектувальники можуть передбачати зміни.[1] |
SOLID |
---|
принципи об'єктно-орієнтованого проектування |
Тому при проектуванні системи необхідно закласти в неї можливість безболісного додавання функціоналу чи його зміни.
Історія
Історично склалося, що термін «принцип відкритості/закритості» має два значення. Обидва використовують успадкування для вирішення дилеми, проте вони різняться цілями, способами та результатами.
Принцип відкритості/закритості Мейєра
Бертран Мейєр відомий як основоположник терміну «принцип відкритості/закритості», що з'явився в 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")) {
// код для аналізу NBA
} else {
if (data[0].equals("nhl")) {
// код для аналізу 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) {
// код для аналізу NBA
}
public SportInfo getResult() {
return result;
}
}
Спочатку визначається парсер, що може обробити вхідний масив. Відношення між ним та лігою встановлюється іншим класом (це може бути шаблон проектування Abstract Factory або IoC контейнер). Варто відмітити, що при додаванні нової ліги, потреби в редагуванні вже робочого коду відпадають, натомість необхідно створити новий клас, що буде реалізовувати інтерфейс SportInfoBuilder
.
Переваги та недоліки
Перевагою використання цього принципу є те, що він дозволяє додавати новий функціонал без редагування вже написаного коду, отже система стає гнучкою та легко супроводжується. Недолік полягає в зростанні кількості класів, що приводить до зростання складності системи.
Використання
SOLID — буква «O» означає принцип відкритості/закритості (англ. Open Closed Principle).
OCP та SRP
OCP та SRP дуже близько пов'язані. Справді, адже чим більше функціональності закладено в клас, тим більша ймовірність того, що його доведеться змінювати. Тому, порушуючи SRP, програміст, найпевніше, порушує і OCP.
Примітки
- McConnell, Steve. (1993). Code Complete. Microsoft Press. ISBN 978-1-55615-484-3.
- Мартін, Роберт «The Open-Closed Principle», C++ Report, January 1996
Посилання
- OOD Principles (рос.)