Antecedents feed

In the last video, Little Light used the strategy model to create customer feedback activities. Not to mention, ridership has increased considerably.

However, along with it, there are also many voices from customers:

  • Can I have coke with ice
  • Mung bean soup with some sugar
  • .

We all have different needs. Some want ice and some don’t, some sugar and some don’t… Light with the customer’s opinion, began to drink the road of improvement.

To improve the road

The first plan

Soon, Xiao Guang came up with the first set of solutions: I regarded the drinks with ice and without ice as two different drinks. With the help of the beverage machine designed in the factory method mode last time, it could be easily expanded to meet the different needs of users without modifying the original implementation.

Light’s first plan:

However, before it was put into use, Xiao Guang found a problem:

From time to time, I get some different drinks from the beverage merchant, and each one may need ice, sugar, or honey… Well, I don’t have to do anything else. I have enough to do with these soda machines.

Decorative appearance

So what’s a better way? Light fell into meditation again. At this time, the cousin came over, jubilant, see the light silly look, asked: “cousin, what do you think, see I just bought a new flower good-looking?” Small light: “how did you change head to spend again?” Cousin: “girls, of course, you have to change patterns to dress up and decorate yourself, isn’t it nice?” “Good-looking good-looking.” small light perfunctory answer.

Suddenly, the light seemed to remember something and murmured, “Dress up” and “decorate “,” yes, I can also decorate my drink with ice, sugar and honey.”

Said to do, xiao Guang started from coke:

Good interface oriented programming habits, small light abstracted a Drink class Drink

public interface Drink {

    String make();
}Copy the code

Achieve a coke:

Public class Coke implements Drink {@override public String make() {return "this is a Coke "; }}Copy the code

A Drink with ice should also be a Drink. Therefore, the ice decoration also realizes the Drink interface. The decoration with ice is based on the Drink, so we also hold a Drink object:

public class Ice implements Drink { private Drink originalDrink; public Ice(Drink originalDrink) { this.originalDrink = originalDrink; } @override public String make() {return originaldrink.make () + "; }}Copy the code

Here’s an experiment:

public class XiaoGuang { public static void main(String[] args) { Drink coke = new Coke(); System.out.println(coke.make()); Drink iceCoke = new Ice(new Coke()); System.out.println(iceCoke.make()); }}Copy the code

Results:

This is a coke. This is a coke with a block of iceCopy the code

Continuous improvement

Little light saw the results of the experiment, laughed, the experiment is successful, and see I promote to all drinks, all ingredients.

Xiao Guang thought that the additive might be sugar, ice, honey, and many other unknown ingredients. In order to meet the open and close principle, I’d better abstract an ingredient first:

public abstract class Stuff implements Drink { private Drink originalDrink; public Stuff(Drink originalDrink) { this.originalDrink = originalDrink; } @override public String make() {return originaldrink.make () + "+ stuffName(); } abstract String stuffName(); }Copy the code

Ice material changed to inherit Stuff:

public class Ice extends Stuff { public Ice(Drink originalDrink) { super(originalDrink); } @override String stuffName() {return "ice "; }}Copy the code

Light also added Sugar Sugar, and Honey Honey raw material.

The specific code is not posted here, the whole code is here.

Put into use

After a lot of verification, little light put the new program into use, and now this is what it looks like…

“Boss, a coke on the rocks.”

Drink iceCoke = new Ice(new Coke()); System.out.println(iceCoke.make()); Here is a coke on the side with iceCopy the code

“Boss, drink X, on the rocks, with sugar.”

Drink iceSugarXDrink = new Ice(new Sugar(new XDrink())); System.out.println(iceSugarXDrink.make()); It's brand X, one sugar, one iceCopy the code

“Coke, double ice, honey.”

Drink doubleIceHoneyCoke = new Ice(new Ice(new Honey(new Coke()))); System.out.println(doubleIceHoneyCoke.make()); It's a coke, one honey, one ice, one iceCopy the code

Perfect performance. Xiao Guang is no longer afraid of strange topping requests from customers.

After the story

As usual, after the story, we use UML class diagrams to tease out the above relationships:

There are two notable relationships in the Stuff class:

  1. Our Stuff also implements the Drink interface, which is to explain that the added Stuff is still a Drink.
  2. Stuff also aggregates an originalDrink instance to illustrate that the material is added to the Drink.

Yes, this is a standard decorator pattern class diagram.

The decorator pattern is used to dynamically assign additional responsibilities to objects. Drink is the decorated object, and Stuff, as the decorated class, can dynamically add features and responsibilities to the decorated object.

Extended Reading 1

Some of you might say, well, I can extend the parent class by adding responsibilities to subclasses by inheriting relationships. Yes, that’s right, inheritance is meant to extend.

However, the biggest difference between decorator pattern and subclass inheritance extension is:

The decorator pattern emphasizes dynamic extension, while inheritance relationships are static.

Due to the static nature of the inheritance mechanism, we create a subclass for each extension responsibility, such as IceCoke, DoubleIceCoke, SugarXDrink, IceSugarXDrink, and so on… It can cause a quasi-explosion.

In addition, a new object-oriented programming principle is introduced here: composition is better than inheritance, so feel for yourself.

Extended Reading 2

Some students say that this on-demand customization mode seems to be similar to Builder mode, so why not use Builder mode?

Here is the essential difference between the two:

Builder mode is a design mode of creation. It aims to solve the problem of object differentiation construction. Decorator pattern is a structural design pattern. Designed to handle combinatorial relationships between objects and classes.

In fact, we could have used the Builder pattern in this example, but just like with inheritance, there were some problems. First, the Builder pattern is to build objects, so it actually requires us to know in advance what properties/responsibilities are available. This allows us to choose different Build methods when building objects. In other words:

The differentiated construction of Builder pattern is predictable, while decorator pattern actually provides an unforeseeable extension composition relationship.

For example, if the user asks for a drink X with honey, in Builder mode we might look like this:

XDrink xDrink = new XDrink.Builder()
        .withHoney()
        .build()Copy the code

But what if after the drink is taken out, the user wants more ice? This is the limitation of Builder mode in this dynamic scaling scenario. See how the decorator mode works:

HoneyXDrink = new Honey(new XDrink()); System.out.println(honeyXDrink.make()); This is brand X with honey // Drink iceHoneyXDrink = new Ice(honeyXDrink); System.out.println(iceHoneyXDrink.make()); It's brand X, one honey, one iceCopy the code

Just garnish the ice with honeyXDrink.

Also, as you can see, decorators (ice, honey, sugar) can be used to decorate all kinds of Drink objects, rather than just specific objects (like cola), thanks to our interface-oriented programming approach. Among the benefits we experience next.

Extension Reading 3

The decorator pattern, as we’ve been saying for a long time, is to dynamically assign additional responsibilities to objects, which actually include decorations (properties) as well as behaviors (methods).

The example we discussed above, for example, is simply to add some modification to the drink, is the drink is programmed to drink with ice, drink with sugar. We can also add behavior to objects, for example, a mobile phone, we can add infrared remote control function through decoration mode, and we can add NFC payment function to it.

Android’s famous Context system is actually the embodiment of the decorator mode, which extends the Context with a series of functions. As follows:

It is clear that the typical practice of using decorator patterns to extend functionality is more focused on extending functionality than our example, reflecting the open closed principle.

Again, object-oriented programming is an idea, and design patterns are the concrete practices and manifestations of these ideas. Learning design patterns allows us to better understand programming ideas, rather than apply them to them. Our goal is to win without losing.

In addition, design patterns are not used singly in specific environments, but are often integrated with various design patterns. For example, in our decorator, various new objects can actually be processed using the factory method pattern we studied.

Ok, let’s use our imagination and get more ingredients to decorate our drinks and maybe we can make a drink that has never been tasted before, haha.

Series of articles here: the road to set up shop – design mode