
Принцип «открыто-закрыто» (OCP) является одним из 5 принципов проектирования SOLID. Его популяризировал американский ученый по информатике и преподаватель Роберт С. Мартин (он же Дядя Боб) в статье, опубликованной в 2000 году.
Остальные 4 принципа дизайна SOLID:
- Принцип единой ответственности (SRP)
- Принцип замены Лескова (LSP)
- Принцип сегрегации интерфейса (ISP)
- Принцип инверсии зависимостей (DIP).
Если вы хотите узнать все эти принципы, вы можете прочитать мой учебник по принципам SOLID здесь.
В этой статье я покажу вам, что означает открытый-закрытый принцип (OCP), почему вы должны использовать его и его реализацию в JavaScript.
Что мы покроем
Что такое принцип «открыто-закрыто»?
Принцип «открыто-закрыто» утверждает, что программные сущности (классы, модули, функции и т.п.) должны быть открыты для расширения, но закрыты для модификации.
Вам, вероятно, интересно, почему это утверждение звучит как противоречие. К примеру, почему что-то открывается и закрывается одновременно?
Что ж, в стране разработки ПО возможно, чтобы элемент открывался для расширения и закрывался для модификации. Это означает, что вы или члены вашей команды должны иметь возможность добавлять новые функции в существующую систему программного обеспечения, не изменяя существующий код.
Принцип «открыто-закрыто» побуждает разработчиков программного обеспечения сосредотачиваться на том, что необходимо, когда приходит время добавлять новые функции.
Если вы хотите добавить новую функциональность к своему существующему коду и вам нужно изменить его, прежде чем добавить новую функциональность, тогда вы не соблюдаете принцип «открыто-закрыто».
Почему следует использовать принцип «открыто-закрыто»?
Вот несколько причин, почему следует использовать принцип «открыто-закрыто».
-
Вам не нужно заново изобретать велосипед: как указано в принципе, код, над которым работаете вы и ваша команда, закрыт для расширения. Это означает, что если вы соблюдаете принцип «открыто-закрыто», вам не нужно изобретать колесо (и строить все заново), когда вы хотите добавить новые функции.
-
Вы сосредотачиваетесь на том, что необходимо: как указано в OCP, ваш код закрыт для модификации. Это означает, что вы можете добавлять новые функции, не слишком редактируя существующий код или вообще не редактируя его. Это может помочь вам и членам вашей команды сосредоточиться на том, что необходимо, когда придет время внедрения новых функций.
-
Вы можете избежать ошибок: поскольку вам не нужно редактировать существующий код перед добавлением новых функций, вы можете избежать ввода ненужных ошибок.
—Ваш код более пригоден для обслуживания, тестирования и гибкости.: соблюдение OCP сделает вашу кодовую базу слабо связанной. Благодаря этому код становится более гибким и подходящим для обслуживания. И если вы хотите, вы можете успешно протестировать каждый класс.
Как реализовать принцип «открыто-закрыто» в JavaScript
Первый пример открыто-закрытого принципа, который я покажу, это класс, использующий переключатель или несколько операторов if. Это потому, что в коде, подобном этому, существует очень реальная возможность сменить класс с помощью операторов switch или if. И это нарушает принцип «открыто-закрыто» в процессе.
class Footballer {
constructor(name, age, role) {
this.name = name;
this.age = age;
this.role = role;
}
getFootballerRole() {
switch (this.role) {
case 'goalkeeper':
console.log(`The footballer, ${this.name} is a goalkeeper`);
break;
case 'defender':
console.log(`The footballer, ${this.name} is a defender`);
break;
case 'midfielder':
console.log(`The footballer, ${this.name} is a midfielder`);
break;
case 'forward':
console.log(`The footballer, ${this.name} plays in the forward line`);
break;
default:
throw new Error(`Unsupported animal type: ${this.type}`);
}
}
}
const kante = new Footballer('Ngolo Kante', 31, 'midfielder');
const hazard = new Footballer('Eden Hazard', 32, 'forward');
kante.getFootballerRole(); // The footballer, Ngolo Kante is a midfielder
hazard.getFootballerRole(); // The footballer, Eden Hazard plays in the forward line
Есть больше футбольных ролей, таких как вингер, опорный полузащитник. Итак, в коде выше, что, по-вашему, случилось бы, если бы вам пришлось добавить другую роль футболиста на поле? Вам придется сменить switch
заявление. Это нарушает принцип «открыто-закрыто», поскольку вам нужно изменить существующий код.
Чтобы устранить нарушения, нужно создать отдельную роль class
использовать метод, который получает роль игрока от супер class
. Но это не кончается. Затем вам нужно создать другой class
для каждой роли, расширяющей класс, получающий роль игрока.
Это будет иметь больше смысла в коде:
class Footballer {
constructor(name, age, role) {
this.name = name;
this.age = age;
this.role = role;
}
getRole() {
return this.role.getRole();
}
}
// PlayerRole class uses the getRole method
class PlayerRole {
getRole() {}
}
// Sub classes for different roles extend the PlayerRole class
class GoalkeeperRole extends PlayerRole {
getRole() {
return 'goalkeeper';
}
}
class DefenderRole extends PlayerRole {
getRole() {
return 'defender';
}
}
class MidfieldRole extends PlayerRole {
getRole() {
return 'midfielder';
}
}
class ForwardRole extends PlayerRole {
getRole() {
return 'forward';
}
}
// Putting all of them together
const hazard = new Footballer('Hazard', 32, new ForwardRole());
console.log(`${hazard.name} plays in the ${hazard.getRole()} line`); // Hazard plays in the forward line
const kante = new Footballer('Ngolo Kante', 31, new MidfieldRole());
console.log(`${kante.name} is the best ${kante.getRole()} in the world!`); //Ngolo Kante is the best midfielder in the world!
Вот еще один пример, использующий функции вместо классов JavaScript:
function calculatePrice(price, discount) {
if (discount === '10%') {
return price * 0.9;
} else if (discount === '20%') {
return price * 0.8;
} else if (discount === '30%') {
return price * 0.7;
} else {
throw new Error('Invalid discount');
}
}
const discountedPrice = calculatePrice(100, '10%');
console.log(`Your discounted price is ${discountedPrice}`); // The discount you get is 90
Приведенный выше код нарушает принцип «открыто-закрыто», потому что вам нужно добавить другой if…else
заявление, если вы хотите добавить новую скидку.
Чтобы исправить это, вы можете получить все ваши скидки на объект и использовать их в функции следующим образом:
const discounts = {
'10%': 0.9,
'20%': 0.8,
'30%': 0.7,
};
function calculatePrice(price, discountType) {
const discount = discounts[discountType];
if (discount === undefined) {
throw new Error('Invalid discount');
}
return price * discount;
}
const discountedPrice = calculatePrice(100, '30%');
console.log(`Your discounted price is $${discountedPrice}`);
Теперь, если вы хотите добавить новые скидки, вам нужно добавить только объект скидки, а не существующую функцию, которая вычисляет скидку.
Вывод
В этой статье вы узнали о принципе «открыто-закрыто», его преимуществах и способах его реализации.
Чтобы выполнить реализацию, мы использовали классы JavaScript, чтобы проиллюстрировать, как можно это сделать с помощью объектно-ориентированного программирования JavaScript. Сначала я показал, как код нарушает принцип и как его рефакторить, чтобы этого не было.
Поскольку функция является программной сущностью, мы также рассмотрели, как можно реализовать принцип «открыто-закрыто» с помощью функций JavaScript.
Продолжайте кодировать!