Перейти к основному содержимому

SOLID

SOLID — это набор принципов объектно-ориентированного проектирования, которые помогают создавать гибкий, поддерживаемый и масштабируемый код. Каждая буква в аббревиатуре соответствует одному принципу.

Single responsibility

Принцип единственной ответственности декларирует, что каждая программная сущность должна выполнять одну (концептуально, а не буквально) задачу.

На практике проявляется в декомпозиции сложных классов, которые выполняют сразу несколько задач, на простые.

Нарушение:

class Order {
public calculateTotal(): number {}
public saveToDatabase(): Promise<void> {}
}

Решение:

class Order {
public calculateTotal(): number {}
}
class OrderRepository {
public save(order: Order): Promise<void> {}
}

Open-closed

Принцип открытости/закрытости декларирует, что программные сущности должны быть открыты для расширения, но закрыты для изменения.

На практике проявляется в том, что программное обеспечение изменяется не через изменение существующего кода, а через добавление нового кода.

Liskov substitution

Принцип подстановки Лисков декларирует, что код, который использует базовый тип, должен иметь возможность использовать подтипы базового типа не зная об этом.

На практике проявляется в том, что при построении иерархий наследования создаваемые наследники должны корректно реализовывать поведение базового типа.

Нарушение:

class Rectangle {
public setWidth(w: number) { this.width = w; }
public setHeight(h: number) { this.height = h; }
}
class Square extends Rectangle {
public setWidth(w: number) { this.width = w; this.height = w; }
}

Решение:

abstract class Shape {
// ...
}
class Rectangle extends Shape {
public setWidth(w: number) { this.width = w; }
public setHeight(h: number) { this.height = h; }
}
class Square extends Shape {
public setSide(s: number) { this.side = s; }
}

Interface segregation

Принцип разделения интерфейса декларирует, что клиенты не должны зависеть от методов интерфейса, которые они не используют.

На практике проявляется в том, что вместо одного большого интерфейса создаются несколько небольших, специфических интерфейсов.

Нарушение:

interface Printer {
print(): void;
scan(): void;
}

class BasicPrinter implements Printer {
public print() {}
public scan() { throw new Error('Scan is not supported'); }
}
class AdvancedPrinter implements Printer {
public print() {}
public scan() {}
}

Решение:

interface Printable {
print(): void;
}
interface Scannable {
scan(): void;
}

class BasicPrinter implements Printable {
public print() {}
}
class AdvancedPrinter implements Printable, Scannable {
public print() {}
public scan() {}
}

Dependency inversion

Принцип инверсии зависимостей декларирует, что модули верхнего уровня не должны зависеть от модулей нижнего уровня; оба должны зависеть от абстракций. При этом абстракции не должны зависеть от деталей; детали должны зависеть от абстракций.

На практике проявляется в том, что зависимости передаются извне (через конструктор, методы или свойства), а не создаются внутри. Это позволяет подменять реализации без изменения кода.

Нарушение:

function analyzeData() {
const logger = new ConsoleLogger();
// ...
}

Решение:

interface Logger {
// ...
}
function analyzeData(logger: Logger) {
// ...
}