Ответ
В своей работе я применяю все пять принципов SOLID для создания поддерживаемого и расширяемого кода.
-
Принцип единственной ответственности (Single Responsibility Principle, SRP): Каждый класс должен иметь одну и только одну причину для изменения. Например, я разделяю класс, отвечающий за данные пользователя, и класс, отвечающий за отправку уведомлений.
// Плохо: Класс делает две вещи class UserService { public void saveUser(User user) { /*...*/ } public void sendEmail(User user) { /*...*/ } } // Хорошо: Ответственности разделены class UserRepository { public void save(User user) { /*...*/ } } class NotificationService { public void sendWelcomeEmail(User user) { /*...*/ } } -
Принцип открытости/закрытости (Open/Closed Principle, OCP): Классы должны быть открыты для расширения, но закрыты для модификации. Я достигаю этого через абстракции и полиморфизм.
interface DiscountStrategy { double applyDiscount(double price); } class RegularDiscount implements DiscountStrategy { /*...*/ } class PremiumDiscount implements DiscountStrategy { /*...*/ } class Order { private DiscountStrategy discount; // Добавление новой скидки не требует изменения класса Order } -
Принцип подстановки Барбары Лисков (Liskov Substitution Principle, LSP): Объекты в программе должны быть заменяемыми на экземпляры их подтипов без изменения правильности программы. Наследник не должен ужесточать предусловия или ослаблять постусловия родителя.
// Нарушение LSP: Квадрат не может заменить Прямоугольник, если у того есть сеттеры для ширины и высоты по отдельности. // Соблюдение LSP: Наследование используется только при истинном отношении "is-a". -
Принцип разделения интерфейса (Interface Segregation Principle, ISP): Много специализированных интерфейсов лучше, чем один универсальный. Клиенты не должны зависеть от методов, которые они не используют.
// Вместо одного "толстого" интерфейса: // interface Worker { void work(); void eat(); } // Я создаю раздельные: interface Workable { void work(); } interface Eatable { void eat(); } class Human implements Workable, Eatable { /*...*/ } class Robot implements Workable { /*...*/ } // Robot не зависит от Eatable -
Принцип инверсии зависимостей (Dependency Inversion Principle, DIP): Модули верхнего уровня не должны зависеть от модулей нижнего уровня. Оба должны зависеть от абстракций. Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций. На практике я внедряю зависимости через конструктор, используя интерфейсы.
interface Database { void save(Data data); } class MySQLDatabase implements Database { /*...*/ } class MongoDB implements Database { /*...*/ } class OrderService { private final Database db; // Зависимость от абстракции public OrderService(Database db) { this.db = db; } public void process(Order order) { db.save(order.getData()); } }
Следование этим принципам помогает мне писать код, который легче тестировать, рефакторить и адаптировать к новым требованиям.