Владеешь ли принципами SOLID
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Владение принципами 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 приложений.