JavaRush /Java Blog /Random EN /Java Decorator Pattern

Java Decorator Pattern

Published in the Random EN group
In the book “Head First. Design Patterns” by Eric Freeman and Elizabeth Robson defines it as follows: The Decorator pattern dynamically provides new capabilities to an object and is an alternative to subclassing for extending functionality. Let's try to look at this definition in more detail using an example. Suppose you have created yet another modern religion and plan to provide people with the corresponding services. Because modern trends pursue vegetarianism, ecology, human development, and for some reason people are not satisfied with “traditional” religions (or atheism in the end), then you, following the mainstream, create another New Age religion (a kind of synthesis from existing religions, taking from each what like). At the beginning you provide the following services: 1. Fortune telling 2. Horoscope Ie. everything looks like this: There is a service interface with a price of course :) and a description
public interface Service {
    public double getPrice();
    public String getLabel();
}
and services
public class Divination implements Service {
    private String label;
    private double price;

    public Divination(String label, double price) {
        this.label = label;
        this.price = price;
    }

    public double getPrice() {
        return this.price;
    }

    public String getLabel() {
        return this.label;
    }
}
public class Horoscope implements Service {
    private String label;
    private double price;

    public Horoscope(String label, double price) {
        this.label = label;
        this.price = price;
    }

    public double getPrice() {
        return this.price;
    }

    public String getLabel() {
        return this.label;
    }
}
Well, accordingly, 2 orders appeared (fortune telling with Tarot cards and a personal horoscope from the client):
public static void main(String[] args) {
    double cost;
    // Гадание на Таро
    Service taro = new Divination("Таро", 1000);
    // И персональный гороскоп
    Service personalHoroscope = new Horoscope("Персональный гороскоп", 9000);
    cost = taro.getPrice() + personalHoroscope.getPrice();

    System.out.println(cost);
}
and the result of the program:
10000.0
Everything would be fine, but there are already hundreds, if not thousands, like you, and we need to continue to develop the spirituality of people, otherwise they are no longer interested. Therefore, additional options to the current services were offered as an option. For example, when choosing a fortune telling service (Tarot or coffee grounds), as an additional option you can order the characteristics of the chakras or aura (with their own cost for each). How could this be implemented so as not to make changes to the existing service classes, where everything is already configured and calculated correctly. You can create additional classes Divination + Chakras or Divination + Aura to the current Divination:
public class Divination implements Service {
    // Здесь своя стоимость и другие методы
}
public class DivinationWithChakras implements Service {
    // Здесь своя стоимость и другие методы
}
public class DivinationWithAura implements Service {
    // Здесь своя стоимость и другие методы
}
or just use subclassing, i.e. extend parent class to child class
public class DivinationWithAura extends Divination {
    public DivinationWithAura(String label, double price) {
        super(label, price);
    }
    // Здесь своя стоимость и другие методы
}
public class DivinationWithChakras extends Divination {
    public DivinationWithChakras(String label, double price) {
        super(label, price);
    }
    // Здесь своя стоимость и другие методы
}
But the disadvantages are immediately visible, developing the spirituality of the whole world, we may have new additional options, which means new classes, and if we still need to combine current ones, then the classes will grow rapidly, at least we no longer have enough of the Fortune telling class with two options together, and not separately separately:
public class DivinationWithChakrasAndAura implements Service {
    // Здесь своя стоимость и другие методы
}
This is where you can use the very “saving” Decorator pattern in Java. To do this, we will create a class for additional options, which will also implement Service, but also contain Service. And accordingly, when we need to place an order for fortune telling and even with 2 options together, it will look like this: The interface is as it was from the very beginning
public interface Service {
    public double getPrice();
    public String getLabel();
}
2 classes of services, the same as before:
public class Divination implements Service {
    private String label;
    private double price;

    public Divination(String label, double price) {
        this.label = label;
        this.price = price;
    }

    public double getPrice() {
        return this.price;
    }

    public String getLabel() {
        return this.label;
    }
}
public class Horoscope implements Service {
    private String label;
    private double price;

    public Horoscope(String label, double price) {
        this.label = label;
        this.price = price;
    }

    public double getPrice() {
        return this.price;
    }

    public String getLabel() {
        return this.label;
    }
}
Decorator for additional options
public class OptionDecorator implements Service {
    private Service service;
    private String label;
    private double price;

    public OptionDecorator(Service service, String label, double price) {
        this.service = service;
        this.label = label;
        this.price = price;
    }

    public double getPrice() {
        return this.price + service.getPrice();
    }

    public String getLabel() {
        return this.label + service.getLabel();
    }
}
And the options themselves (2 so far):
public class Aura extends OptionDecorator {
    public Aura(Service service) {
        super(service, "Характеристика ауры", 1500);
    }
}
public class Chakra extends OptionDecorator {
    public Chakra(Service service) {
        super(service, "Характеристика чакр", 500);
    }
}
Well, the order itself
public static void main(String[] args) {
    // Гадание на Таро
    Service taro = new Divination("Таро", 1000);
    Service chakra = new Chakra(taro);
    Service aura = new Aura(chakra);

    // И общая стоимость
    System.out.println(aura.getPrice());
}
3000.0
which gives the result of the sum of the main service and 2 additional options. This means that there is no need for every additional option (or a combination of both) to create a new class. In addition, these options can be applied not only to the Fortune telling service, but also to the Horoscope service. Therefore, when in the near future we need to implement the following additional options: - compatibility of avatar partners on social networks - improving cash flow through remote channeling, we will need to write only 2 additional classes:
public class Channeling extends OptionDecorator {
    public Channeling(Service service) {
        super(service, "Полет в Поле Чудес", 99999);
    }
}
public class Avatar extends OptionDecorator {
    public Avatar(Service service) {
        super(service, "Ваша любовь в соц сетях", 5555);
    }
}
and you can add them to any service:
public static void main(String[] args) {
    // Гадание на Таро
    Service taro = new Divination("Таро", 1000);
    Service chakra = new Chakra(taro);
    Service aura = new Aura(chakra);

    // И общая стоимость
    System.out.println(aura.getPrice());

    // Гороскоп
    Service horoscope = new Horoscope("Персональный гороскоп", 1000);
    Service channenling = new Channeling(horoscope);
    Service avatar = new Avatar(channenling);

    // И общая стоимость
    System.out.println(avatar.getPrice());
}
and the result of the program (which we need):
3000.0
106554.0
Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION