0 x0, introduction

Near the end of the week, I will read a section on the Beauty of Design Patterns, which correspond to design patterns and paradigms: structural (50) and Decorator Pattern.

The decorator pattern has a very similar code structure to the ** bridge pattern (separating entities and behaviors) from the previous section, extending the existing class with composition, but solving very different problems.

Tips: Secondary knowledge processing inevitably has mistakes, interested in the time can refer to the original, thank you.


0 x1, definition

Allowing you to dynamically add new functionality to an existing object without changing its structure is a wrapper around the existing object.

Well understood, it is a set of layers, unlike the proxy mode, decorator mode can be a set of layers like a doll.

Write an example

Bridge mode → breadth, decorator mode → depth, how to reflect? Again, the shape of the previous section:

abstract class Shape {
    abstract void show(a);
}

/ / shape
public class Circle extends Shape {
    @Override void show(a) { System.out.println("Circular"); }}public class Square extends Shape {
    @Override void show(a) { System.out.println("Rectangle"); }}// Introduce color
public class RedCircle extends Shape {
    @Override void show(a) { System.out.println("Red circle"); }}public class RedSquare extends Shape {
    @Override void show(a) { System.out.println("Red rectangle"); }}public class BlueCircle extends Shape {
    @Override void show(a) { System.out.println("Blue circle"); }}public class BlueSquare extends Shape {
    @Override void show(a) { System.out.println("Blue rectangle"); }}Copy the code

Last time there were more shapes or colors, now there are more needs, like adding texture: matte and smooth

public class SmoothRedCircle extends RedCircle// Smooth red circlepublic class SmoothRedSquare extends RedSquare// Smooth red rectanglepublic class SmoothBlueCircle extends BlueCircle// Smooth blue circlepublic class SmoothBlueSquare extends BlueSquare// Smooth blue rectanglepublic class MatteRedCircle extends RedCircle// Frosted red circlepublic class MatteRedSquare extends RedSquare// Frosted red rectanglepublic class MatteBlueCircle extends BlueCircle// Frosted blue circlepublic class MatteBlueSquare extends BlueSquare// Frosted blue rectangleCopy the code

Ok, subclass becomes 8, then add size: large, small, subclass becomes 8*3=24, then add? Direct type explosion ~

The decorator pattern is suitable for this kind of deep multi-level inheritance scenario:

// Abstract components (both interfaces and abstract classes work)
interface IShape {
    String show(a);
}

// Abstract decorator class (with an internal reference to the component object, used to call the methods of the preceding object)
public abstract class BaseDecorator implements IShape {
    private IShape shape;
    public BaseDecorator(IShape shape) { this.shape = shape; }
    @Override public String show(a) { returnshape.show(); }}// Concrete component class
public class CircleShape implements IShape {
    @Override public String show(a) { return "Circular"; }}public class SquareShape implements IShape {
    @Override public String show(a) { return "Rectangle"; }}// Color concrete decorator class (you can call the methods defined in the abstract decorator class, or add methods to extend the behavior of the object)
public class RedDecorator extends BaseDecorator {
    public RedDecorator(IShape shape) { super(shape); }
    @Override public String show(a) { return "Red" + super.show(); }}public class BlueDecorator extends BaseDecorator {
    public BlueDecorator(IShape shape) { super(shape); }
    @Override public String show(a) { return "Blue" + super.show(); }}// Material specific decoration class
public class SmoothDecorator extends BaseDecorator {
    public SmoothDecorator(IShape shape) { super(shape); }
    @Override public String show(a) { return "Smooth" + super.show(); }}public class MatteDecorator extends BaseDecorator {
    public MatteDecorator(IShape shape) { super(shape); }
    @Override public String show(a) { return "Frosted" + super.show(); }}// Size specific decoration class
public class BigDecorator extends BaseDecorator {
    public BigDecorator(IShape shape) { super(shape); }
    @Override public String show(a) { return "Big" + super.show(); }}public class MiddleDecorator extends BaseDecorator {
    public MiddleDecorator(IShape shape) { super(shape); }
    @Override public String show(a) { return "In" + super.show(); }}public class SmallDecorator extends BaseDecorator {
    public SmallDecorator(IShape shape) { super(shape); }
    @Override public String show(a) { return "Small" + super.show(); }}// Test case
public class DecoratorTest {
    public static void main(String[] args) {
        IShape circle = new CircleShape();
        IShape square = new SquareShape();
        IShape redCircle =  new RedDecorator(circle);
        IShape smoothBlueSquare = new SmoothDecorator(new BlueDecorator(square));
        IShape bigMatteRedCircle = new BigDecorator(newMatteDecorator(redCircle)); System.out.println(circle.show()); System.out.println(square.show()); System.out.println(redCircle.show()); System.out.println(smoothBlueSquare.show()); System.out.println(bigMatteRedCircle.show()); }}Copy the code

The output is as follows:

With inheritance there are 24 subclasses, and with decorator patterns there are only 10, illustrating the UML class diagram:

Four characters:

  • Component (Abstract Component) → Declare the business methods implemented by the concrete Component so that the client handles decorated and decorated objects in a consistent manner;
  • ConcreteComponent → Concrete implementation of abstract component;
  • Decorator (Abstract Decorator class) → Include references to components and override methods that abstract components;
  • ConcreteDecorator → Concrete implementations of abstract decorator classes that override methods and add additional functionality;

Applicable scenarios:

  • Adding functionality to a single object quickly and transparently without affecting other objects;
  • Scenarios that do not support inheritance of extended classes, such as classes decorated with the final keyword;

Advantages: Rapid extension of object functions, more flexible than inheritance, does not lead to a sharp increase in the number of classes, dynamic addition and deletion of object instances, as required by the sequence combination of functions;

Disadvantages:

  • When chain decorators are called sequentially, deleting a decorator requires modifying the context code;
  • Easy to add many decorative objects, increase the difficulty of understanding agents;
  • As with the bridge pattern, composition is harder than inheritance to find call relationships between objects;

Attached: Sample application of decorator pattern → Java IO Class library

Java IO inheritance tree

Two variable dimensions: byte stream or character stream, InputStream or OutputStream, combine four subclasses: InputStream, OutputStream, Reader and Writer. According to different reading and writing scenarios, many subclasses are extended based on these four parent classes.

In daily use, the concept of low-level streams and high-level streams is also involved. High-level streams cannot be used directly, so they need to be passed into low-level streams, wrapped in several layers, such as:

InputStream in = new FileInputStream("/user/jie/test.txt");
InputStream bin = new BufferedInputStream(in);
DataInputStream din = new DataInputStream(bin);
int data = din.readInt();
Copy the code

The principle is that with the decorator pattern, low-level streams can be enhanced on demand, such as support for caching, and support for reading data from primitive types. You can open these three classes to see:

As you can see, both classes inherit from the InputStream class. The constructors of the two middle classes pass InputStream instances:

Look at the datainputStream.readint () method:

Int is 4 bytes, so we read it 4 times, and the code passed in is BufferedInputStream, which calls its read() method:

Fill () fills the buffer and returns an array of bytes.

In addition, if you haven’t noticed, these two classes don’t inherit directly from InputStream. Instead, they inherit from FilterInputStream.

Some decorators themselves don’t really need to handle methods like read(), but the chain passing of the decorator pattern does not need to implement these methods. And if each of these decorators overrides the method, there’s a lot of duplicate code. Reduce this duplication by providing a default implementation with a decorator parent, FilterInputStream.