Java Design Pattern Series (9) Combination Patterns

Java Design Pattern Series (9) Combination Patterns

Group objects into tree structures to represent part-whole hierarchies. Composition mode enables users to use a single object consistently.

1. Combination mode structure

Figure 9-1 Combination mode structure

  • Component: An abstract component object, which declares an interface for the objects in the composition, so that the client can access and manage the entire object structure through this interface, and can provide the default implementation for the defined functions in it.

  • Leaf: The leaf node object, which defines and implements the behavior of the leaf object, and no longer contains other child node objects.

  • Composite: A composite object that typically stores subcomponents, defines the behavior of those components that contain subcomponents, and implements subcomponent-related operations defined in the component interface.

  • Client: The client operates the component objects in the composite structure through the component interface.

Composite Pattern Reference Implementation

(1) First look at the definition of the component object, the sample code is as follows:

/**
 * 抽象的组件对象,为组合中的对象声明接口,实现接口的缺省行为
 */
public abstract class Component {

    /** 示意方法,子组件对象可能有的功能方法 */
    public abstract void someOperation();

    /** 向组合对象中加入组件对象 */
    public void addChild(Component child) {
        // 缺省的实现,抛出例外,因为叶子对象没有这个功能,或者子组件没有实现这个功能
        throw new UnsupportedOperationException("对象不支持这个功能");
    }

    /** 从组合对象中移出某个组件对象 */
    public void removeChild(Component child) {
        // 缺省的实现,抛出例外,因为叶子对象没有这个功能,或者子组件没有实现这个功能
        throw new UnsupportedOperationException("对象不支持这个功能");
    }

    /** 返回某个索引对应的组件对象 */
    public Component getChildren(int index) {
        // 缺省的实现,抛出例外,因为叶子对象没有这个功能,或者子组件没有实现这个功能
        throw new UnsupportedOperationException("对象不支持这个功能");
    }
}

(2) Next, look at the definition of the Composite object. The sample code is as follows:

/**
 * 组合对象,通常需要存储子对象,定义有子部件的部件行为,
 * 并实现在Component里面定义的与子部件有关的操作
 */
public class Composite extends Component {
    /**
     * 用来存储组合对象中包含的子组件对象
     */
    private List<Component> childComponents = null;

    /** 示意方法,通常在里面需要实现递归的调用 */
    public void someOperation() {
        if (childComponents != null){
            for(Component c : childComponents){
                //递归的进行子组件相应方法的调用
                c.someOperation();
            }
        }
    }
    public void addChild(Component child) {
        //延迟初始化
        if (childComponents == null) {
            childComponents = new ArrayList<Component>();
        }
        childComponents.add(child);
    }

    public void removeChild(Component child) {
        if (childComponents != null) {
            childComponents.remove(child);
        }
    }

    public Component getChildren(int index) {
        if (childComponents != null){
            if(index>=0 && index<childComponents.size()){
                return childComponents.get(index);
            }
        }
        return null;
    }
}

(3) It's time to look at the definition of the leaf object, which is relatively simple. The sample code is as follows:

/**
 * 叶子对象,叶子对象不再包含其它子对象
 */
public class Leaf extends Component {
    /** 示意方法,叶子对象可能有自己的功能方法 */
    public void someOperation() {
        // do something
    }

}

(4) For the Client, the Component interface is used to operate the combined object structure. Due to the various ways of use, here is only a demonstration use, which is used as a test code by the way. The sample code is as follows:

public class Client {
    public static void main(String[] args) {
        //定义多个Composite对象
        Component root = new Composite();
        Component c1 = new Composite();
        Component c2 = new Composite();
        //定义多个叶子对象
        Component leaf1 = new Leaf();
        Component leaf2 = new Leaf();
        Component leaf3 = new Leaf();

        //组和成为树形的对象结构
        root.addChild(c1);
        root.addChild(c2);
        root.addChild(leaf1);

        c1.addChild(leaf2);
        c2.addChild(leaf3);

        //操作Component对象
        Component o = root.getChildren(1);
        System.out.println(o);
    }
}

Second, the parent component reference

Figure 9-2 Parent component reference structure

(1) Component

public abstract class Component {
    /**
     * 记录父组件对象
     */
    private Component parent = null;

    /**
     * 获取一个组件的父组件对象
     * @return 一个组件的父组件对象
     */
    public Component getParent() {
        return parent;
    }
    /**
     * 设置一个组件的父组件对象
     * @param parent 一个组件的父组件对象
     */
    public void setParent(Component parent) {
        this.parent = parent;
    }
    /**
     * 返回某个组件的子组件对象
     * @return 某个组件的子组件对象
     */
    public List<Component> getChildren() {
        throw new UnsupportedOperationException("对象不支持这个功能");
    }

    /*-------------------以下是原有的定义----------------------*/

    /**
     * 输出组件自身的名称
     */
    public abstract void printStruct(String preStr);

    /**
     * 向组合对象中加入组件对象
     * @param child 被加入组合对象中的组件对象
     */
    public void addChild(Component child) {
        // 缺省的实现,抛出例外,因为叶子对象没有这个功能,或者子组件没有实现这个功能
        throw new UnsupportedOperationException("对象不支持这个功能");
    }

    /**
     * 从组合对象中移出某个组件对象
     * @param child 被移出的组件对象
     */
    public void removeChild(Component child) {
        // 缺省的实现,抛出例外,因为叶子对象没有这个功能,或者子组件没有实现这个功能
        throw new UnsupportedOperationException("对象不支持这个功能");
    }

    /**
     * 返回某个索引对应的组件对象
     * @param index 需要获取的组件对象的索引,索引从0开始
     * @return 索引对应的组件对象
     */
    public Component getChildren(int index) {
        throw new UnsupportedOperationException("对象不支持这个功能");
    }
}

(2) Leaf

public class Leaf extends Component {
    /**
     * 叶子对象的名字
     */
    private String name = "";

    /**
     * 构造方法,传入叶子对象的名字
     * @param name 叶子对象的名字
     */
    public Leaf(String name){
        this.name = name;
    }

    /**
     * 输出叶子对象的结构,叶子对象没有子对象,也就是输出叶子对象的名字
     * @param preStr 前缀,主要是按照层级拼接的空格,实现向后缩进
     */
    public void printStruct(String preStr){
        System.out.println(preStr+"-"+name);
    }
}

(3) Composite

public class Composite extends Component{

    public void addChild(Component child) {
        //延迟初始化
        if (childComponents == null) {
            childComponents = new ArrayList<Component>();
        }
        childComponents.add(child);

        //添加对父组件的引用
        child.setParent(this);
    }

    public void removeChild(Component child) {
        if (childComponents != null) {
            //查找到要删除的组件在集合中的索引位置
            int idx = childComponents.indexOf(child);
            if (idx != -1) {
                //先把被删除的商品类别对象的父商品类别,设置成为被删除的商品类别的子类别的父商品类别
                for(Component c : child.getChildren()){
                    //删除的组件对象是本实例的一个子组件对象
                    c.setParent(this);
                    //把被删除的商品类别对象的子组件对象添加到当前实例中
                    childComponents.add(c);
                }

                //真的删除
                childComponents.remove(idx);
            }
        }
    }

    public List<Component> getChildren() {
        return childComponents;
    }

    /*-------------------以下是原有的实现,没有变化----------------------*/

    /**
     * 用来存储组合对象中包含的子组件对象
     */
    private List<Component> childComponents = null;

    /**
     * 组合对象的名字
     */
    private String name = "";

    /**
     * 构造方法,传入组合对象的名字
     * @param name 组合对象的名字
     */
    public Composite(String name){
        this.name = name;
    }

    /**
     * 输出组合对象自身的结构
     * @param preStr 前缀,主要是按照层级拼接的空格,实现向后缩进
     */
    public void printStruct(String preStr){
        //先把自己输出去
        System.out.println(preStr+"+"+this.name);
        //如果还包含有子组件,那么就输出这些子组件对象
        if(this.childComponents!=null){
            //然后添加一个空格,表示向后缩进一个空格
            preStr+=" ";
            //输出当前对象的子对象了
            for(Component c : childComponents){
                //递归输出每个子对象
                c.printStruct(preStr);
            }
        }
    }
}

3. Summary

(1) The essence of the observer pattern

The essence of the composition pattern: unify the leaf object and the composition object.

The combination mode treats the leaf object as a special combination object, so that the leaf object and the combination object are treated equally, and all are regarded as the Component object, which organically unifies the leaf object and the combination object.

It is precisely because the leaf object and the combined object are unified that there is no need to make a distinction when building the object into a tree structure. Anyway, the component object contains other component objects, and so on recursively; The operation becomes simple, regardless of the object type, unified operation.

(2) When to choose the observer mode

  • If you want to represent a part-whole hierarchy of objects, you can use the composition pattern to unify the operations of the whole and the parts, making the hierarchy easier to implement and easier to use from the outside.

  • If you want to use all the objects in the composite structure uniformly, you can choose the composite mode, which is the main function provided by the composite mode.

(3) Advantages and disadvantages of the observer pattern

  • Defines a class hierarchy containing base and composite objects

    In the combination mode, basic objects can be combined into more complex combined objects, and combined objects can be combined into more complex combined objects, which can be recursively combined to form a unified class hierarchy of combined objects

  • Unify composite objects and leaf objects

    In the composition mode, leaf objects can be regarded as special composition objects, and a unified parent class can be defined for them, so as to unify the behavior of composition objects and leaf objects.

  • Simplified client calls

    The combination mode unifies the combination of objects and leaf objects, so that the client does not need to distinguish them when using them. The client does not care what type of object is used, which greatly simplifies the use of the client.

  • easier to expand

    Since the client operates uniformly on the Component, a newly defined Composite or Leaf subclass can easily work with the existing structure, and the client does not need to change to add a new component class

  • Difficulty limiting the types of components in a composition

    The ease of adding new components also brings problems, such as the difficulty of limiting the types of components in the composition. When we need to detect component types, we cannot rely on compile-time type constraints to complete, and must be dynamically detected during runtime.


Record a little bit every day. Content may not be important, but habits are!

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325859755&siteId=291194637