JavaRush /Java Blog /Random EN /Java Decorator Pattern (Decorator)

Java Decorator Pattern (Decorator)

Published in the Random EN group
In the book “Head First. Design Patterns” by Eric Freeman and Elizabeth Robson defines the Decorator pattern as dynamically empowering an object and is an alternative to subclassing in terms of extending functionality. Let's try to consider this definition in more detail with an example. Suppose you have created one next modern religion and plan to provide people with relevant services. Because modern trends are pursuing vegetarianism, ecology, human development, and for some reason “traditional” religions (or atheism in the end) do not suit people, 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. Divination 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 on 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 output of the program:
10000.0
Everything would be fine, but there are already hundreds, if not thousands, of people like you, and you need to continue to develop the spirituality of people, otherwise they are no longer interested. Therefore, as an option, additional options for current services were proposed. So, for example, when choosing a divination service (Tarot or coffee grounds), as an additional option, you can order a characteristic of chakras or aura (with its 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 correctly calculated. 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
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 you still need to combine the current ones, then the classes will grow rapidly, at least there is not enough Divination class with two options together, and not individually:
public class DivinationWithChakrasAndAura implements Service {
    // Здесь своя стоимость и другие методы
}
Here 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 the Service, but also contain the 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 the same as it was from the very beginning
public interface Service {
    public double getPrice();
    public String getLabel();
}
2 classes of services, as it was 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 (so far 2 pieces):
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
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. So, it is not necessary for each additional. option (or a combination of them) 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 partners in avatars in social networks - improvement of cash flow through remote channeling, we 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