Тест на вакансию

SOLID принципы на примерах Java

9 сентября 2025 г.
216

SRP - Single Responsibility Principle (Принцип единственной ответственности)

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

Плохой пример:
public class Employee {
    private String name;
    private String position;
    private double salary;
    
    // Конструкторы, геттеры, сеттеры...
    
    public void saveToDatabase() {
        // Сохранение в базу данных
    }
    
    public void calculateTax() {
        // Расчет налогов
    }
    
    public void generateReport() {
        // Генерация отчета
    }
}
Хороший пример:
public class Employee {
    private String name;
    private String position;
    private double salary;
    
    // Конструкторы, геттеры, сеттеры
}

public class EmployeeRepository {
    public void save(Employee employee) {
        // Сохранение в базу
    }
}

public class TaxCalculator {
    public double calculateTax(Employee employee) {
        // Расчет налогов
        return employee.getSalary() * 0.13;
    }
}

public class ReportGenerator {
    public void generateReport(Employee employee) {
        // Генерация отчета
    }
}

OCP - Open-Closed Principle (Принцип открытости/закрытости)

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

Плохой пример:
public class AreaCalculator {
    public double calculateArea(Object shape) {
        if (shape instanceof Circle) {
            Circle circle = (Circle) shape;
            return Math.PI * circle.getRadius() * circle.getRadius();
        } else if (shape instanceof Rectangle) {
            Rectangle rectangle = (Rectangle) shape;
            return rectangle.getWidth() * rectangle.getHeight();
        }
        throw new IllegalArgumentException("Unknown shape");
    }
}
Хороший пример:
public interface Shape {
    double calculateArea();
}

public class Circle implements Shape {
    private double radius;
    
    public Circle(double radius) {
        this.radius = radius;
    }
    
    @Override
    public double calculateArea() {
        return Math.PI * radius * radius;
    }
}

public class Rectangle implements Shape {
    private double width;
    private double height;
    
    public Rectangle(double width, double height) {
        this.width = width;
        this.height = height;
    }
    
    @Override
    public double calculateArea() {
        return width * height;
    }
}

public class AreaCalculator {
    public double calculateArea(Shape shape) {
        return shape.calculateArea();
    }
}

LSP - Liskov Substitution Principle (Принцип подстановки Лисков)

Объекты должны быть заменяемыми на экземпляры их подтипов без изменения правильности программы.

Плохой пример:
public class Rectangle {
    protected int width;
    protected int height;
    
    public void setWidth(int width) {
        this.width = width;
    }
    
    public void setHeight(int height) {
        this.height = height;
    }
    
    public int getArea() {
        return width * height;
    }
}

public class Square extends Rectangle {
    @Override
    public void setWidth(int width) {
        super.setWidth(width);
        super.setHeight(width); // Нарушает поведение прямоугольника
    }
    
    @Override
    public void setHeight(int height) {
        super.setHeight(height);
        super.setWidth(height); // Нарушает поведение прямоугольника
    }
}
Хороший пример:
public interface Shape {
    int getArea();
}

public class Rectangle implements Shape {
    private int width;
    private int height;
    
    public Rectangle(int width, int height) {
        this.width = width;
        this.height = height;
    }
    
    @Override
    public int getArea() {
        return width * height;
    }
}

public class Square implements Shape {
    private int side;
    
    public Square(int side) {
        this.side = side;
    }
    
    @Override
    public int getArea() {
        return side * side;
    }
}

ISP - Interface Segregation Principle (Принцип разделения интерфейсов)

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

Плохой пример:
public interface Worker {
    void work();
    void eat();
    void sleep();
}

public class HumanWorker implements Worker {
    public void work() { /* работа */ }
    public void eat() { /* еда */ }
    public void sleep() { /* сон */ }
}

public class RobotWorker implements Worker {
    public void work() { /* работа */ }
    public void eat() { /* робот не ест! */ }
    public void sleep() { /* робот не спит! */ }
}
Хороший пример:
public interface Workable {
    void work();
}

public interface Eatable {
    void eat();
}

public interface Sleepable {
    void sleep();
}

public class HumanWorker implements Workable, Eatable, Sleepable {
    public void work() { /* работа */ }
    public void eat() { /* еда */ }
    public void sleep() { /* сон */ }
}

public class RobotWorker implements Workable {
    public void work() { /* работа */ }
}

DIP - Dependency Inversion Principle (Принцип инверсии зависимостей)

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

Плохой пример:
public class EmailService {
    public void sendEmail(String message) {
        // Отправка email
    }
}

public class Notification {
    private EmailService emailService;
    
    public Notification() {
        this.emailService = new EmailService(); // Жесткая зависимость
    }
    
    public void sendNotification(String message) {
        emailService.sendEmail(message);
    }
}
Хороший пример:
public interface MessageService {
    void sendMessage(String message);
}

public class EmailService implements MessageService {
    public void sendMessage(String message) {
        // Отправка email
    }
}

public class SMSService implements MessageService {
    public void sendMessage(String message) {
        // Отправка SMS
    }
}

public class Notification {
    private MessageService messageService;
    
    // Внедрение зависимости через конструктор
    public Notification(MessageService messageService) {
        this.messageService = messageService;
    }
    
    public void sendNotification(String message) {
        messageService.sendMessage(message);
    }
}

// Использование
public class Main {
    public static void main(String[] args) {
        MessageService emailService = new EmailService();
        Notification notification = new Notification(emailService);
        notification.sendNotification("Hello!");
        
        // Легко заменить на SMS
        MessageService smsService = new SMSService();
        Notification smsNotification = new Notification(smsService);
        smsNotification.sendNotification("Hello via SMS!");
    }
}

Практический пример с применением всех принципов

// SRP: Каждый класс имеет одну ответственность
// OCP: Легко добавить новые типы платежей
// LSP: Все платежные системы взаимозаменяемы
// ISP: Интерфейсы разделены по функциональности
// DIP: Зависимости от абстракций

public interface PaymentProcessor {
    void processPayment(double amount);
}

public interface PaymentValidator {
    boolean validatePayment();
}

public interface PaymentNotifier {
    void sendNotification();
}

public class CreditCardPayment implements PaymentProcessor, PaymentValidator {
    public void processPayment(double amount) {
        // Обработка платежа по карте
    }
    
    public boolean validatePayment() {
        // Валидация карты
        return true;
    }
}

public class PayPalPayment implements PaymentProcessor, PaymentNotifier {
    public void processPayment(double amount) {
        // Обработка PayPal платежа
    }
    
    public void sendNotification() {
        // Отправка уведомления PayPal
    }
}

public class PaymentService {
    private PaymentProcessor paymentProcessor;
    
    public PaymentService(PaymentProcessor paymentProcessor) {
        this.paymentProcessor = paymentProcessor;
    }
    
    public void makePayment(double amount) {
        paymentProcessor.processPayment(amount);
    }
}
Эти принципы помогают создавать гибкий, поддерживаемый и расширяемый код, который легко тестировать и модифицировать.
Поделиться: