Sobes.tech
Middle+
84
questionbank

Как ты применяешь принцип открытости-закрытости из SOLID при проектировании своих решений?

Ответ от нейросети

sobes.tech AI

Я применяю принцип открытости/закрытости (Open/Closed Principle - OCP), фокусируясь на расширяемости без модификации существующего кода.

Ключевые подходы:

  • Использование абстракций (интерфейсов и абстрактных классов): Мой код зависит от абстракций, а не от конкретных реализаций. Это позволяет добавлять новые реализации без изменения существующего кода, использующего эти абстракции.

    // Интерфейс, представляющий контракт
    public interface PaymentProcessor {
        void processPayment(BigDecimal amount);
    }
    
    // Конкретная реализация
    public class CreditCardPaymentProcessor implements PaymentProcessor {
        @Override
        public void processPayment(BigDecimal amount) {
            // Логика обработки кредитной карты
            System.out.println("Processing credit card payment: " + amount);
        }
    }
    
    // Другая реализация
    public class PayPalPaymentProcessor implements PaymentProcessor {
        @Override
        public void processPayment(BigDecimal amount) {
            // Логика обработки PayPal
            System.out.println("Processing PayPal payment: " + amount);
        }
    }
    
    // Класс, использующий абстракцию, закрыт для модификации, но открыт для расширения новыми PaymentProcessor
    public class Order {
        private PaymentProcessor paymentProcessor;
    
        public Order(PaymentProcessor paymentProcessor) {
            this.paymentProcessor = paymentProcessor;
        }
    
        public void finalizeOrder(BigDecimal totalAmount) {
            paymentProcessor.processPayment(totalAmount);
            // Дополнительная логика заказа
        }
    }
    
  • Стратегия (Strategy Pattern): Этот шаблон проектирования является прямым воплощением OCP. Он позволяет определить семейство алгоритмов, инкапсулировать каждый из них и сделать их взаимозаменяемыми.

    // Интерфейс стратегии
    interface ShippingStrategy {
        BigDecimal calculateCost(BigDecimal weight);
    }
    
    // Конкретная стратегия
    class StandardShipping implements ShippingStrategy {
        @Override
        public BigDecimal calculateCost(BigDecimal weight) {
            return weight.multiply(new BigDecimal("2.5"));
        }
    }
    
    // Другая стратегия
    class ExpressShipping implements ShippingStrategy {
        @Override
        public BigDecimal calculateCost(BigDecimal weight) {
            return weight.multiply(new BigDecimal("5.0")).add(new BigDecimal("10.0"));
        }
    }
    
    // Контекст, использующий стратегию, закрыт для модификации, но открыт для новых стратегий
    class Order {
        private ShippingStrategy shippingStrategy;
    
        public Order(ShippingStrategy shippingStrategy) {
            this.shippingStrategy = shippingStrategy;
        }
    
        public BigDecimal getShippingCost(BigDecimal weight) {
            return shippingStrategy.calculateCost(weight);
        }
    }
    
  • Шаблонный метод (Template Method Pattern): Определяет скелет алгоритма в суперклассе, оставляя детали реализации подклассам. Это позволяет изменять шаги алгоритма без изменения его структуры.

    // Абстрактный класс с шаблонным методом
    abstract class DataProcessor {
        // Шаблонный метод
        public final void processData() {
            readData();
            transformData();
            writeData();
        }
    
        // Абстрактные методы для переопределения в подклассах
        abstract void readData();
        abstract void transformData();
        abstract void writeData();
    }
    
    // Конкретный процессор, расширяющий шаблон
    class CSVDataProcessor extends DataProcessor {
        @Override
        void readData() {
            System.out.println("Reading data from CSV...");
        }
    
        @Override
        void transformData() {
            System.out.println("Transforming CSV data...");
        }
    
        @Override
        void writeData() {
            System.out.println("Writing data to database...");
        }
    }
    
  • Фабричные методы (Factory Methods): Позволяют создавать объекты, предоставляя интерфейс для создания, но оставляя выбор типа объекта подклассам. Это добавляет гибкость в создании объектов без изменения клиента, использующего фабрику.

    interface Product {
        void use();
    }
    
    class ConcreteProductA implements Product {
        @Override
        public void use() {
            System.out.println("Using Product A");
        }
    }
    
    class ConcreteProductB implements Product {
        @Override
        public void use() {
            System.out.println("Using Product B");
        }
    }
    
    abstract class Creator {
        public void operation() {
            Product product = factoryMethod();
            product.use();
        }
    
        // Фабричный метод
        protected abstract Product factoryMethod();
    }
    
    class ConcreteCreatorA extends Creator {
        @Override
        protected Product factoryMethod() {
            return new ConcreteProductA();
        }
    }
    
    class ConcreteCreatorB extends Creator {
        @Override
        protected Product factoryMethod() {
            return new ConcreteProductB();
        }
    }
    

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