Принципы SOLID коротко и простым языком (на JS)
SOLID это аббревиатура, которая обозначает пять принципов объектно-ориентированного программирования. Эти принципы помогают разработчикам писать качественный и поддерживаемый код. Вот как выглядят эти принципы:
Single Responsibility Principle (Принцип единственной ответственности) - Каждый класс должен иметь одну ответственность (функционал) и все его методы должны быть связаны с этой ответственностью. Например:
class User {
constructor(name, email) {
this.name = name;
this.email = email;
}
getName() {
return this.name;
}
getEmail() {
return this.email;
}
setEmail(email) {
this.email = email;
}
}
Open/Closed Principle (Принцип открытости/закрытости) - Классы должны быть открыты для расширения, но закрыты для модификации. Например:
class Shape {
area() {
throw new Error('Area method should be implemented');
}
}
class Circle extends Shape {
constructor(radius) {
super();
this.radius = radius;
}
area() {
return Math.PI * this.radius * this.radius;
}
}
class Rectangle extends Shape {
constructor(width, height) {
super();
this.width = width;
this.height = height;
}
area() {
return this.width * this.height;
}
}
В этом примере, мы создали абстрактный класс Shape
, который содержит абстрактный метод area
. Далее, мы реализовали этот метод в классах Circle
и Rectangle
, таким образом расширяя функциональность абстрактного класса Shape
, но не меняя его код. Это демонстрирует принцип открытости/закрытости.
Liskov Substitution Principle (Принцип подстановки Барбары Лисков) - Объекты в программе должны быть заменяемыми на экземпляры их подтипов, не нарушая правильность программы. Например:
class Animal {
eat() {}
sleep() {}
play() {}
}
class Cat {
eat() {}
sleep() {}
}
class Dog {
eat() {}
play() {}
}
В примере выше, класс Cat
и Dog
не используют метод play
, но они все равно наследуют его от класса Animal
, что нарушает принцип разделения интерфейса. Вместо этого, мы можем разделить интерфейс Animal
на несколько меньших интерфейсов:
interface Eatable {
eat();
}
interface Sleepable {
sleep();
}
interface Playable {
play();
}
class Cat implements Eatable, Sleepable {}
class Dog implements Eatable, Playable {}
Таким образом, класс Cat
реализует только те методы, которые он действительно использует, а класс Dog
реализует те методы, которые он использует.
И последний принцип: Dependency Inversion Principle (Принцип инверсии зависимостей) - Зависимости в программе должны быть направлены от абстракций к реализациям, а не наоборот. Например:
class User {
constructor(database) {
this.database = database;
}
save() {
this.database.save();
}
}
class MySQLDatabase {
save() {
console.log('Saving to MySQL database');
}
}
const user = new User(new MySQLDatabase());
user.save(); // Output: 'Saving to MySQL database'
В примере выше, класс User
не зависит от конкретной реализации класса MySQLDatabase
, а зависит от абстракции Database
. Это позволяет нам легко заменить реализацию базы данных на другую, не меняя код класса User
.