组合模式(十七)

一、定义

    组合模式(Composite Pattern)也叫合成模式,有时又叫做部分-整体模式(Part-Whole),主要是用来描述部分与整体的关系,其定义如下:
    将对象组合成树形结构以表示“部分-整体”的层次结构,使得用户对单个对象和组合对象的使用具有一致性。


二、角色

  1. Component
    是组合中的对象声明接口,在适当的情况下,实现所有类共有接口的默认行为。声明一个接口用于访问和管理Component子部件。
  2. Leaf
    在组合中表示叶子节点对象,叶子结点没有子节点。
  3. Composite
    定义树枝节点行为,用来存储子节点,在 Component 接口中实现与子部件有关操作,如增加(add)和删除(remove)等。


三、使用场景

  1. 维护和展示部分-整体关系的场景,如树形菜单、文件和文件夹管理。
  2. 希望用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象。


四、实例分析

    组合模式非常适合“树”形结构,例如文件夹系统就是一个典型的“树”行结构,文件夹就是 Composite ,子文件即 Leaf,它们都属于文件(Component)。这里,我们可以定义一个 TreeNode 类模拟“树”形结构,它同时饰演 ComponentLeafComposite 三个角色。

1. TreeNode

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
 
public class TreeNode {
    public static final String NODE_SEPARATOR = ":";
 
    private int mId;
    private int mLastId;
    private TreeNode mParent;
    private final List<TreeNode> children;
    private Object mValue;
 
    public static TreeNode root() {
        TreeNode root = new TreeNode(null);
        return root;
    }
 
    private int generateId() {
        return ++mLastId;
    }
 
    public TreeNode(Object value) {
        children = new ArrayList<>();
        mValue = value;
    }
 
    public TreeNode addChild(TreeNode childNode) {
        childNode.mParent = this;
        childNode.mId = generateId();
        children.add(childNode);
        return this;
    }
 
    public TreeNode addChildren(TreeNode... nodes) {
        for (TreeNode n : nodes) {
            addChild(n);
        }
        return this;
    }
 
    public TreeNode addChildren(Collection<TreeNode> nodes) {
        for (TreeNode n : nodes) {
            addChild(n);
        }
        return this;
    }
 
    public int deleteChild(TreeNode child) {
        for (int i = 0; i < children.size(); i++) {
            if (child.mId == children.get(i).mId) {
                children.remove(i);
                return i;
            }
        }
        return -1;
    }
 
    public List<TreeNode> getChildren() {
        return Collections.unmodifiableList(children);
    }
 
    public int size() {
        return children.size();
    }
 
    public TreeNode getParent() {
        return mParent;
    }
 
    public int getId() {
        return mId;
    }
 
    public boolean isLeaf() {
        return size() == 0;
    }
 
    public Object getValue() {
        return mValue;
    }
 
    public String getPath() {
        final StringBuilder path = new StringBuilder();
        TreeNode node = this;
        while (node.mParent != null) {
            path.append(node.getId());
            node = node.mParent;
            if (node.mParent != null) {
                path.append(NODE_SEPARATOR);
            }
        }
        return path.toString();
    }
 
    public int getLevel() {
        int level = 0;
        TreeNode root = this;
        while (root.mParent != null) {
            root = root.mParent;
            level++;
        }
        return level;
    }
 
    public boolean isRoot() {
        return mParent == null;
    }
 
    public TreeNode getRoot() {
        TreeNode root = this;
        while (root.mParent != null) {
            root = root.mParent;
        }
        return root;
    }
 
}

2. 模拟一个公司结构,定义一个 Staff

public class Staff {
    private String name; // 姓名
    private String position; // 职位
    public Staff(String name, String position) {
        this.name = name;
        this.position = position;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getPosition() {
        return position;
    }
    public void setPosition(String position) {
        this.position = position;
    }
    @Override
    public String toString() {
        return "Staff [name=" + name + ", position=" + position + "]";
    }
}

3. 模拟一个场景

public class Main {
    public static void main(String[] args) {
        TreeNode ceo = new TreeNode(new Staff("小马哥", "CEO"));
        TreeNode developBoss = new TreeNode(new Staff("张三", "开发老大"));
        TreeNode financeBoss = new TreeNode(new Staff("李四", "财务总管"));
        TreeNode coderA = new TreeNode(new Staff("路人甲", "码农"));
        TreeNode coderB = new TreeNode(new Staff("路人乙", "码农"));
        TreeNode financeA = new TreeNode(new Staff("路人丙", "会计"));
        // 他们的关系是
        ceo.addChild(developBoss);
        ceo.addChild(financeBoss);
        developBoss.addChild(coderA);
        developBoss.addChild(coderB);
        financeBoss.addChild(financeA);
        // 我们可以轻松获得任一一个节点的上司以及他的小弟
        Staff parent = (Staff) developBoss.getParent().getValue();
        System.out.println("developBoss 的老大是:" + parent.toString());
        List<TreeNode> chilren = developBoss.getChildren();
        for (TreeNode treeNode : chilren) {
            Staff child = (Staff) treeNode.getValue();
            System.out.println("developBoss 的小弟是:" + child.toString());
        }
        // 随时来一个新人,都可以把他加到“树”中的某个位置
        TreeNode salesBoss = new TreeNode(new Staff("王二", "销售经理"));
        salesBoss.addChild(new TreeNode(new Staff("路人丁", "推销员")));
        ceo.addChild(salesBoss);
    }
}

4. 运行结果

developBoss 的老大是:Staff [name=小马哥, position=CEO]
developBoss 的小弟是:Staff [name=路人甲, position=码农]
developBoss 的小弟是:Staff [name=路人乙, position=码农]

5. 总结分析

    上例中的  TreeNode 类模拟了一个类似于“树”的结构,它既可以是一个中间节点(树枝),也可以是一个子节点(叶子),取决于它的 children 的大小是不是为 0。同时,它持有一个 Object 的引用,这个 Object 可以是任一一个类的实例,这样  TreeNode 类的适用性就很强了,毕竟万物皆对象嘛。


查看更多:设计模式分类以及六大设计原则

猜你喜欢

转载自blog.csdn.net/afei__/article/details/80716667