Design mode 4 decorator mode

Keywords: Design Pattern

definition

Decorator Pattern is to dynamically attach responsibilities to objects. To extend functionality, decorators offer a more flexible alternative than inheritance—— HEAD First design pattern

Main role

Abstract decorated Component: the most primitive object that needs to be decorated. Such as the coverage class (super class) in the example.
Concrete decorated ConretetComponent: a concrete Component that implements the abstract methods in the Component abstract class through inheritance. It's the one to decorate. Like the single coffee in the example.
Abstract Decorator: an abstract class. In its properties, there must be a private variable pointing to the Component abstract Component. This is an example of all kinds of milk and other spices.
Concrete decorator

give an example

It is derived from the Head FIRST design pattern to realize the coffee shop business.
Coffee types: Espresso, ShortBlack (concentrated), LongBlack, Decaf (sugar free)
Spices include Milk, Soy and Chocolate, that is, add Milk, Chocolate and so on to the basic varieties.
First, define the Beverage class, which is the abstract class of all drinks and the class to be decorated:

public abstract class Beverage {
	public String description="Unknown Beverage"; //This description extends to specific items or condiments
	public String getDescription()
	{
		return description;
	}
	public abstract double cost();  //This is abstract because the price can be returned directly in a single variety

So is the seasoning class:

public  class CondimentDecorator extends Beverage {
	//All condiment decorators must re implement the getDescription() method to get the overall description of the selected beverage recursively
	public abstract String getDescription();
}

With these two base classes, we need to implement some drinks:
Espresso:

public class Espresso extends Beverage {
    public Espresso() {
        description = "Espresso";
    }
	//Return Espresso price
    @Override
    public BigDecimal cost() {
        return new BigDecimal("1.99");
    }
}

Deep roasted coffee:

public class DarkRoast extends Beverage {
    public DarkRoast() {
        description = "DarkRoast";
    }
	//Returns the price of dark toast
    @Override
    public BigDecimal cost() {
        return new BigDecimal("0.89");
    }
}

Next, implement some spices
Soybean milk seasoning:

public class Soy extends CondimentDecorator {
    //Use an instance variable to record the beverage, that is, the decorator
    Beverage beverage;
	//Pass in the drink as a parameter and record it in the variable
    public Soy(Beverage beverage) {
        this.beverage = beverage;
    }
	//Add Soy description on the basis of the original beverage (the original beverage is added with Soy sauce and decorated with Soy sauce)
    @Override
    public String getDescription() {
        return beverage.getDescription() + ",Soy";
    }
	//Add the price of Soy sauce to the original beverage (the original beverage is decorated with Soy sauce)
    @Override
    public BigDecimal cost() {
        return new BigDecimal("0.3").add(beverage.cost());
    }
}

Similarly, define a milk bubble seasoning:

public class Whip extends CondimentDecorator {
    Beverage beverage;
    public Whip(Beverage beverage) {
        this.beverage = beverage;
    }
    public String getDescription() {
        return beverage.getDescription() + ",Whip";
    }
    public BigDecimal cost() {
        return new BigDecimal("0.42").add(beverage.cost());
    }
}

This defines two basic drinks and two spices. In the actual order, a cup of deep roasted coffee beverage with soybean milk and milk foam is required:

public class StarbuzzCoffee {
	public static void main(String[] args) {
		Beverage beverage = new DarkRoast();
		beverage = new Soy(beverage);
		beverage = new Whip(beverage);
		System.out.println("Description: " + beverage.getDescription() + " $" + beverage.cost());
	}
}

The operation result is:

Description: darkroast,Soy,Whip $1.61

Usage scenario

When class inheritance will cause class explosion and is not conducive to later maintenance, decorator mode can be adopted. For example, milk tea can be seasoned at will.
When you need to dynamically add functionality to an object and can dynamically undo it, you can use decorator mode.

Advantages and disadvantages

advantage

  • Decorated classes and decorated classes can develop independently without coupling each other. It effectively separates the core responsibility of the class from the decorating function
  • Decoration pattern can dynamically extend the function of an implementation class
  • It embodies the opening and closing principle: classes should be open to extensions and closed to modifications

shortcoming

Multi layer decoration is more complex. For example, we now have many layers of decoration. If something goes wrong, check it layer by layer

Decorator Pattern instance in Java -- Java IO

Drawing source: Design mode (III) -- decorator mode

InputStream is an abstract component that needs to be decorated; FileInputStream, StringBufferInputStream and ByteArrayStream are specific components wrapped by decorators.
FileInputStream and other three are some subjects. FilterInputStream is an abstract decorator, and the following are some specific decorators.

Java IO example

To convert all input to lowercase:

//Extend FilterInputStream
public class LowerCaseInputStream extends FilterInputStream{
	protected LowerCaseInputStream(InputStream in) {
		super(in);
	}
	//Similar to the cost method, it implements two read methods, one for byte array and one for byte array
	public int read() throws IOException
	{
		int c = super.read();//Call the topic object of super(in) above
		return c == -1 ? c : Character.toLowerCase((char)(c));
	}
	public int read(byte[] b,int offset,int len) throws IOException//For byte array
	{
		int result = super.read(b,offset,len);
		for(int i = offset;i < offset + result; i++)
		{
			b[i] = (byte)Character.toUpperCase((char)(b[i]));
		}
		return result;
	}
}

Then use this decorator mode:

public class InputTest {
	public static void main(String[] args) {
		int c;
		try {
			InputStream in = new LowerCaseInputStream(new BufferedInputStream(new FileInputStream("test.txt")));
			while((c = in.read()) >= 0)
			{
				System.out.print((char)c);
			}
			in.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

reference resources

Head FIRST design pattern
Design mode (III) -- decorator mode
Decorator mode of design mode
On design mode -- decoration mode
Decorator mode of design mode

Posted by chawezul on Mon, 25 Oct 2021 07:33:52 -0700