← Назад к вопросам

Приведи пример использования паттерна декоратор

3.0 Senior🔥 11 комментариев
#Docker, Kubernetes и DevOps

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Паттерн Декоратор (Decorator Pattern)

Декоратор — это структурный паттерн проектирования, который позволяет динамически добавлять новую функциональность к объектам, оборачивая их в специальные объекты-обёртки. Это альтернатива наследованию, которая работает более гибко.

Зачем нужен декоратор

Представь: у тебя есть класс Coffee (кофе). Ты можешь:

  • Добавить молоко
  • Добавить сахар
  • Добавить карамель
  • Комбинировать все вместе

Если использовать наследование, создастся взрыв классов: CoffeeWithMilk, CoffeeWithSugar, CoffeeWithMilkAndSugar, и т.д. Декоратор решает эту проблему элегантно.

Пример с кодом

// Базовый интерфейс
public interface Beverage {
    double getCost();
    String getDescription();
}

// Конкретная реализация
public class Coffee implements Beverage {
    @Override
    public double getCost() {
        return 2.0; // $2
    }

    @Override
    public String getDescription() {
        return "Coffee";
    }
}

// Абстрактный декоратор
public abstract class BeverageDecorator implements Beverage {
    protected Beverage beverage;

    public BeverageDecorator(Beverage beverage) {
        this.beverage = beverage;
    }
}

// Конкретный декоратор 1: Молоко
public class MilkDecorator extends BeverageDecorator {
    public MilkDecorator(Beverage beverage) {
        super(beverage);
    }

    @Override
    public double getCost() {
        return beverage.getCost() + 0.5;
    }

    @Override
    public String getDescription() {
        return beverage.getDescription() + ", Milk";
    }
}

// Конкретный декоратор 2: Сахар
public class SugarDecorator extends BeverageDecorator {
    public SugarDecorator(Beverage beverage) {
        super(beverage);
    }

    @Override
    public double getCost() {
        return beverage.getCost() + 0.25;
    }

    @Override
    public String getDescription() {
        return beverage.getDescription() + ", Sugar";
    }
}

// Конкретный декоратор 3: Ванильный сироп
public class VanillaSyrupDecorator extends BeverageDecorator {
    public VanillaSyrupDecorator(Beverage beverage) {
        super(beverage);
    }

    @Override
    public double getCost() {
        return beverage.getCost() + 0.75;
    }

    @Override
    public String getDescription() {
        return beverage.getDescription() + ", Vanilla Syrup";
    }
}

Использование

public class CoffeeShop {
    public static void main(String[] args) {
        // Просто кофе
        Beverage beverage1 = new Coffee();
        System.out.println(beverage1.getDescription() + " - \$" + beverage1.getCost());
        // Вывод: Coffee - $2.0

        // Кофе с молоком
        Beverage beverage2 = new MilkDecorator(new Coffee());
        System.out.println(beverage2.getDescription() + " - \$" + beverage2.getCost());
        // Вывод: Coffee, Milk - $2.5

        // Кофе с молоком и сахаром
        Beverage beverage3 = new SugarDecorator(new MilkDecorator(new Coffee()));
        System.out.println(beverage3.getDescription() + " - \$" + beverage3.getCost());
        // Вывод: Coffee, Milk, Sugar - $2.75

        // Кофе с молоком, сахаром и ванильным сиропом
        Beverage beverage4 = new VanillaSyrupDecorator(
            new SugarDecorator(
                new MilkDecorator(new Coffee())
            )
        );
        System.out.println(beverage4.getDescription() + " - \$" + beverage4.getCost());
        // Вывод: Coffee, Milk, Sugar, Vanilla Syrup - $3.5
    }
}

Преимущества паттерна

  • Гибкость: добавляй функции динамически в любое время
  • Комбинируемость: комбинируй декораторы в любом порядке
  • Не нарушает открыто-закрытый принцип (OCP): расширяем функциональность без изменения исходного класса
  • Избегаем взрыва классов: вместо N×M комбинаций у нас есть модульный подход

Реальные примеры в Java

  • java.io пакет: BufferedInputStream, DataInputStream — это декораторы для InputStream
  • Spring: @Transactional, @Cacheable — декораторы функциональности
  • Подготовка напитков в кофейне: как в нашем примере

Недостатки

  • Может привести к множеству маленьких классов
  • Порядок декораторов иногда имеет значение
  • Сложнее отладить длинные цепочки декораторов

Вывод: Декоратор — мощный паттерн для добавления функциональности без наследования. Используй его, когда нужно комбинировать поведение гибко.