Learn a design pattern every day: the synthesis pattern of structural pattern

Keywords: Java

1, Basic concepts

The composition pattern belongs to the structural pattern of objects, sometimes called "part whole" pattern. The composition pattern organizes objects into a tree structure, which can be used to describe the relationship between the whole and the part. The composition mode enables the client to treat simple elements as compound elements.

2, Popular explanation

Composite mode: Mary's birthday today. "I have a present for my birthday." "Well, well, go to the store and choose for yourself." "This T-shirt is very beautiful. Buy it. This skirt is nice. Buy it. This bag is also good. Buy it." "Hello, I bought three. I only promised to give one gift." "What, T-shirt, skirt and bag, just match into a set, miss, please wrap it up." “……” MM can use composite mode, can you? Composition mode: composition mode organizes objects into a tree structure, which can be used to describe the relationship between the whole and the part. The composite pattern is a pattern that deals with the tree structure of objects. The composition pattern expresses the relationship between the part and the whole in a tree structure. The composition mode enables the client to treat each individual component object as the composition object composed of them.

File system example:

The composition pattern represents the relationship between the part and the whole in a tree structure. The composition mode enables the client to treat each individual component object as the composition object composed of them. For example, a file system is a typical composition mode system. The following figure is part of a common computer XP file system.

As can be seen from the above figure, the file system is a tree structure with nodes. There are two kinds of tree nodes, one is the branch node, that is, the directory, which has the internal tree structure and is colored in the diagram; the other is the file, which is the leaf node, which has no internal tree structure.

Obviously, directories and files can be treated and processed as the same object, which is the application of compositing mode.

The composition mode can not provide the management method of the parent object, but the composition mode must provide the management method of the child object in the appropriate place, such as: add(), remove(), and getChild().

3, Classification

According to the differences of the implemented interfaces, the implementation of the synthesis mode can be divided into two forms, namely, security and transparency.

1. Structure of safe synthesis mode

The composition mode of security mode requires that the method of managing aggregation only appears in the tree component class, but not in the tree component class.

                                

This form involves three roles:

● abstract Component role: This is an abstract role. It defines a common interface and its default behavior for the objects participating in the composition, which can be used to manage all sub objects. A composite object usually treats its children as objects of type Component. In the safe composition mode, Component role does not define the method of managing sub objects, which is given by branch Component object.

● leaf role: a leaf object is an object without subordinate sub objects, which defines the behavior of the original objects participating in the combination.

● component role: represents the object with subordinate sub objects participating in the combination. The tree component class gives all the methods to manage sub objects, such as add(), remove(), and getChild().

Abstract component role class

public interface Component {
    /**
     * Output the name of the component itself
     */
    public void printStruct(String preStr);
}

Branch component role class

public class Composite implements Component {
    /**
     * Used to store sub component objects contained in composite objects
     */
    private List<Component> childComponents = new ArrayList<Component>();
    /**
     * The name of the composite object
     */
    private String name;
    /**
     * Constructor, passing in the name of the composite object
     * @param name    The name of the composite object
     */
    public Composite(String name){
        this.name = name;
    }
    /**
     * Aggregation management method, adding a sub component object
     * @param child Subcomponent object
     */
    public void addChild(Component child){
        childComponents.add(child);
    }
    /**
     * Aggregate management method, delete a sub component object
     * @param index Subscript of subcomponent object
     */
    public void removeChild(int index){
        childComponents.remove(index);
    }
    /**
     * Aggregate management method, return all sub component objects
     */
    public List<Component> getChild(){
        return childComponents;
    }
    /**
     * Output object's own structure
     * @param preStr Prefix, mainly splices spaces according to levels to achieve backward indentation
     */
    @Override
    public void printStruct(String preStr) {
        // Output yourself first
        System.out.println(preStr + "+" + this.name);
        //If there are also subcomponents, the subcomponent objects are output
        if(this.childComponents != null){
            //Add two spaces to indent back two spaces
            preStr += "  ";
            //Output child objects of current object
            for(Component c : childComponents){
                //Recursively output each sub object
                c.printStruct(preStr);
            }
        }        
    }
}

Leaf component role class

public class Leaf implements Component {
    /**
     * Name of leaf object
     */
    private String name;
    /**
     * Constructor, passing in the name of the leaf object
     * @param name Name of leaf object
     */
    public Leaf(String name){
        this.name = name;
    }
    /**
     * Output the structure of the leaf object. The leaf object has no sub objects, that is, the name of the leaf object
     * @param preStr Prefix, mainly refers to the space spliced according to the level to achieve backward indentation
     */
    @Override
    public void printStruct(String preStr) {
        // TODO Auto-generated method stub
        System.out.println(preStr + "-" + name);
    }
}

Client class

public class Client {
    public static void main(String[]args){
        Composite root = new Composite("clothing");
        Composite c1 = new Composite("men's wear");
        Composite c2 = new Composite("Women's wear");
        
        Leaf leaf1 = new Leaf("shirt");
        Leaf leaf2 = new Leaf("Jacket");
        Leaf leaf3 = new Leaf("skirt");
        Leaf leaf4 = new Leaf("suit");
        
        root.addChild(c1);
        root.addChild(c2);
        c1.addChild(leaf1);
        c1.addChild(leaf2);
        c2.addChild(leaf3);
        c2.addChild(leaf4);
        
        root.printStruct("");
    }
}

It can be seen that the tree component class (Composite) gives the declaration and implementation of addChild(), removeChild(), getChild(), and other methods, while the leaf component class does not give the declaration or implementation of these methods. This is a safe approach. Because of this feature, it is impossible for client applications to call the aggregation methods of leaf components by mistake, because without these methods, the calling will lead to compilation errors.

The disadvantage of the safe compositing mode is that it is not transparent enough, because the leaf class and the branch class will have different interfaces.

2. Structure of transparent synthesis mode

Different from the safe synthesis mode, the transparent synthesis mode requires that all concrete component classes, no matter the branch component or the leaf component, conform to a fixed interface.

                                         

Abstract component role class

public abstract class Component {
    /**
     * Output the name of the component itself
     */
    public abstract void printStruct(String preStr);
    /**
     * Aggregation management method, adding a sub component object
     * @param child Subcomponent object
     */
    public void addChild(Component child){
        /**
         * Default implementation, throw exception, because the leaf object does not have this function
         * Or the subcomponent does not implement this function
         */
        throw new UnsupportedOperationException("Object does not support this feature");
    }
    /**
     * Aggregate management method, delete a sub component object
     * @param index Subscript of subcomponent object
     */
    public void removeChild(int index){
        /**
         * Default implementation, throw exception, because the leaf object does not have this function
         * Or the subcomponent does not implement this function
         */
        throw new UnsupportedOperationException("Object does not support this feature");
    }
    
    /**
     * Aggregate management method, return all sub component objects
     */
    public List<Component> getChild(){
        /**
         * Default implementation, throw exception, because the leaf object does not have this function
         * Or the subcomponent does not implement this function
         */
        throw new UnsupportedOperationException("Object does not support this feature");
    }
}

Branch component role class, which changes the implements response to extends response, and there is no change elsewhere.

public class Composite extends Component {
    /**
     * Used to store sub component objects contained in composite objects
     */
    private List<Component> childComponents = new ArrayList<Component>();
    /**
     * The name of the composite object
     */
    private String name;
    /**
     * Constructor, passing in the name of the composite object
     * @param name    The name of the composite object
     */
    public Composite(String name){
        this.name = name;
    }
    /**
     * Aggregation management method, adding a sub component object
     * @param child Subcomponent object
     */
    public void addChild(Component child){
        childComponents.add(child);
    }
    /**
     * Aggregate management method, delete a sub component object
     * @param index Subscript of subcomponent object
     */
    public void removeChild(int index){
        childComponents.remove(index);
    }
    /**
     * Aggregate management method, return all sub component objects
     */
    public List<Component> getChild(){
        return childComponents;
    }
    /**
     * Output object's own structure
     * @param preStr Prefix, mainly splices spaces according to levels to achieve backward indentation
     */
    @Override
    public void printStruct(String preStr) {
        // Output yourself first
        System.out.println(preStr + "+" + this.name);
        //If there are also subcomponents, the subcomponent objects are output
        if(this.childComponents != null){
            //Add two spaces to indent back two spaces
            preStr += "  ";
            //Output child objects of current object
            for(Component c : childComponents){
                //Recursively output each sub object
                c.printStruct(preStr);
            }
        }        
    }
}

Leaf component role class, which changes the implements response to extends response, and there is no change elsewhere.

public class Leaf extends Component {
    /**
     * Name of leaf object
     */
    private String name;
    /**
     * Constructor, passing in the name of the leaf object
     * @param name Name of leaf object
     */
    public Leaf(String name){
        this.name = name;
    }
    /**
     * Output the structure of the leaf object. The leaf object has no sub objects, that is, the name of the leaf object
     * @param preStr Prefix, mainly refers to the space spliced according to the level to achieve backward indentation
     */
    @Override
    public void printStruct(String preStr) {
        // TODO Auto-generated method stub
        System.out.println(preStr + "-" + name);
    }
}

The main change for client classes is that they no longer distinguish between Composite objects and Leaf objects.

public class Client {
    public static void main(String[]args){
        Component root = new Composite("clothing");
        Component c1 = new Composite("men's wear");
        Component c2 = new Composite("Women's wear");
        
        Component leaf1 = new Leaf("shirt");
        Component leaf2 = new Leaf("Jacket");
        Component leaf3 = new Leaf("skirt");
        Component leaf4 = new Leaf("suit");
        
        root.addChild(c1);
        root.addChild(c2);
        c1.addChild(leaf1);
        c1.addChild(leaf2);
        c2.addChild(leaf3);
        c2.addChild(leaf4);
        
        root.printStruct("");
    }
}

It can be seen that the client does not need to distinguish between the Component object and the leaf object; for the client, the Component object is the operation.

4, The choice of two implementation methods

The security composition mode here refers to whether it is more secure from the perspective of using the composition mode on the client side. If it is secure, there will be no possibility of misoperation, and the accessible methods are all supported.

The transparent compositing mode here refers to whether to distinguish between "tree object" and "leaf object" when using the compositing mode from the client. If it is transparent, there is no need to distinguish. For customers, they are all component objects. Specific types are transparent for clients, and they need not be concerned.

For the synthesis mode, in terms of security and transparency, it will pay more attention to transparency. After all, the purpose of the synthesis mode is to make the client no longer distinguish between the tree object and the leaf object, but operate in a unified way.

And for the implementation of security, we need to distinguish whether it is a branch object or a leaf object. Sometimes, when you need to type an object, but find that the type information is lost, you have to force the conversion. This type conversion is not safe enough.

Therefore, when using the synthesis mode, it is recommended to use transparency.  

(Reference: The synthesis mode of JAVA and mode)

Posted by dukeu03 on Tue, 26 May 2020 04:14:02 -0700