← Назад к вопросам

Владеешь ли принципами SOLID

1.0 Junior🔥 201 комментариев
#Soft skills и опыт работы

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI28 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Владение принципами SOLID

Да, я глубоко владею принципами SOLID. Это фундаментальные принципы объектно-ориентированного программирования, которые критичны для написания чистого, поддерживаемого и масштабируемого кода. Давайте разберём каждый принцип на практических примерах.

S — Single Responsibility Principle (SRP)

Классы и функции должны иметь только одну причину для изменения.

// ❌ Плохо: класс делает слишком много
class User {
  id: string;
  name: string;
  email: string;
  
  // Бизнес-логика
  validateEmail() { }
  
  // Логирование
  logLogin() {
    console.log(`User ${this.name} logged in`);
  }
  
  // Сохранение в БД
  save() {
    // SQL query
  }
  
  // Отправка email
  sendWelcomeEmail() {
    // SMTP logic
  }
}

// ✅ Хорошо: разделяем ответственность
class User {
  id: string;
  name: string;
  email: string;
  
  validateEmail(): boolean {
    return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(this.email);
  }
}

class UserRepository {
  async save(user: User): Promise<void> {
    // SQL query в БД
  }
  
  async findById(id: string): Promise<User> {
    // Query from DB
  }
}

class EmailService {
  async sendWelcomeEmail(user: User): Promise<void> {
    // SMTP отправка
  }
}

class UserLogger {
  logLogin(user: User): void {
    console.log(`User ${user.name} logged in`);
  }
}

O — Open/Closed Principle (OCP)

Классы открыты для расширения, но закрыты для модификации.

// ❌ Плохо: нужно изменять класс при добавлении нового типа платежа
class PaymentProcessor {
  process(paymentType: string, amount: number): void {
    if (paymentType === 'credit_card') {
      this.processCreditCard(amount);
    } else if (paymentType === 'paypal') {
      this.processPayPal(amount);
    } else if (paymentType === 'crypto') {
      this.processCrypto(amount);
    }
  }
  
  private processCreditCard(amount: number) { }
  private processPayPal(amount: number) { }
  private processCrypto(amount: number) { }
}

// ✅ Хорошо: используем полиморфизм
interface PaymentMethod {
  process(amount: number): Promise<void>;
}

class CreditCardPayment implements PaymentMethod {
  async process(amount: number): Promise<void> {
    // Credit card logic
  }
}

class PayPalPayment implements PaymentMethod {
  async process(amount: number): Promise<void> {
    // PayPal logic
  }
}

class CryptoPayment implements PaymentMethod {
  async process(amount: number): Promise<void> {
    // Crypto logic
  }
}

class PaymentProcessor {
  async process(method: PaymentMethod, amount: number): Promise<void> {
    await method.process(amount);
  }
}

L — Liskov Substitution Principle (LSP)

Дочерние классы должны корректно заменять родительские без нарушения логики.

// ❌ Плохо: квадрат нарушает контракт Rectangle
class Rectangle {
  width: number;
  height: number;
  
  setWidth(w: number) { this.width = w; }
  setHeight(h: number) { this.height = h; }
  
  getArea(): number {
    return this.width * this.height;
  }
}

class Square extends Rectangle {
  setWidth(w: number) {
    this.width = w;
    this.height = w; // Нарушает контракт!
  }
  
  setHeight(h: number) {
    this.width = h;
    this.height = h; // Нарушает контракт!
  }
}

// ✅ Хорошо: правильная иерархия
interface Shape {
  getArea(): number;
}

class Rectangle implements Shape {
  constructor(private width: number, private height: number) { }
  
  getArea(): number {
    return this.width * this.height;
  }
}

class Square implements Shape {
  constructor(private side: number) { }
  
  getArea(): number {
    return this.side * this.side;
  }
}

I — Interface Segregation Principle (ISP)

Клиенты не должны зависеть от интерфейсов, которые они не используют.

// ❌ Плохо: большой интерфейс
interface UserService {
  getUser(id: string): Promise<User>;
  createUser(data: CreateUserDto): Promise<User>;
  updateUser(id: string, data: UpdateUserDto): Promise<User>;
  deleteUser(id: string): Promise<void>;
  sendEmail(email: string): Promise<void>;
  logActivity(action: string): void;
  generateReport(): Promise<Report>;
}

class BasicUserService implements UserService {
  getUser(id: string): Promise<User> { }
  createUser(data: CreateUserDto): Promise<User> { }
  updateUser(id: string, data: UpdateUserDto): Promise<User> { }
  deleteUser(id: string): Promise<void> { }
  sendEmail(email: string): Promise<void> { } // Не нужно!
  logActivity(action: string): void { } // Не нужно!
  generateReport(): Promise<Report> { } // Не нужно!
}

// ✅ Хорошо: разделяем интерфейсы
interface IUserRepository {
  getUser(id: string): Promise<User>;
  createUser(data: CreateUserDto): Promise<User>;
  updateUser(id: string, data: UpdateUserDto): Promise<User>;
  deleteUser(id: string): Promise<void>;
}

interface IEmailService {
  sendEmail(email: string): Promise<void>;
}

interface ILogger {
  logActivity(action: string): void;
}

interface IReportGenerator {
  generateReport(): Promise<Report>;
}

class UserService implements IUserRepository {
  async getUser(id: string): Promise<User> { }
  async createUser(data: CreateUserDto): Promise<User> { }
  async updateUser(id: string, data: UpdateUserDto): Promise<User> { }
  async deleteUser(id: string): Promise<void> { }
}

D — Dependency Inversion Principle (DIP)

Зависимости должны быть от абстракций, а не от конкретных реализаций.

// ❌ Плохо: прямая зависимость от конкретной реализации
class UserService {
  private emailService = new EmailService();
  private logger = new ConsoleLogger();
  
  async createUser(data: CreateUserDto): Promise<User> {
    const user = new User(data);
    await this.emailService.sendWelcomeEmail(user);
    this.logger.log('User created');
    return user;
  }
}

// ✅ Хорошо: зависимость от интерфейсов через инъекцию
interface IEmailService {
  sendWelcomeEmail(user: User): Promise<void>;
}

interface ILogger {
  log(message: string): void;
}

class UserService {
  constructor(
    private emailService: IEmailService,
    private logger: ILogger
  ) { }
  
  async createUser(data: CreateUserDto): Promise<User> {
    const user = new User(data);
    await this.emailService.sendWelcomeEmail(user);
    this.logger.log('User created');
    return user;
  }
}

// Легко тестировать с mock
const mockEmail: IEmailService = {
  sendWelcomeEmail: jest.fn()
};

const service = new UserService(mockEmail, new ConsoleLogger());

Практическое применение в Node.js

// Правильная архитектура с SOLID

// Domain Layer
class Order {
  id: string;
  items: OrderItem[];
  status: 'pending' | 'confirmed' | 'shipped';
  
  confirm(): void {
    if (this.status !== 'pending') {
      throw new Error('Invalid status');
    }
    this.status = 'confirmed';
  }
}

// Application Layer
interface IOrderRepository {
  save(order: Order): Promise<void>;
  findById(id: string): Promise<Order>;
}

interface IEmailService {
  sendOrderConfirmation(order: Order): Promise<void>;
}

class ConfirmOrderUseCase {
  constructor(
    private orderRepository: IOrderRepository,
    private emailService: IEmailService
  ) { }
  
  async execute(orderId: string): Promise<void> {
    const order = await this.orderRepository.findById(orderId);
    order.confirm();
    await this.orderRepository.save(order);
    await this.emailService.sendOrderConfirmation(order);
  }
}

// Infrastructure Layer
class PostgresOrderRepository implements IOrderRepository {
  async save(order: Order): Promise<void> {
    // SQL query
  }
  
  async findById(id: string): Promise<Order> {
    // SQL query
  }
}

class SmtpEmailService implements IEmailService {
  async sendOrderConfirmation(order: Order): Promise<void> {
    // Send via SMTP
  }
}

// Dependency Injection
const orderRepo = new PostgresOrderRepository();
const emailService = new SmtpEmailService();
const confirmOrder = new ConfirmOrderUseCase(orderRepo, emailService);

Вывод

SOLID принципы — это основа для написания качественного кода:

  • SRP — одна ответственность = легче тестировать
  • OCP — расширяемость без изменений
  • LSP — надёжная иерархия типов
  • ISP — чистые интерфейсы
  • DIP — слабая связанность, тестируемость

Эти принципы работают вместе и обеспечивают Clean Architecture, которая критична для масштабируемых Node.js приложений.