Как ты применяешь принцип открытости-закрытости из 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(); } }
Применяя эти подходы, я создаю код, который легче поддерживать, расширять и тестировать, минимизируя риск внесения ошибок при добавлении новой функциональности.