设计模式:组合模式(Composite)

将对象组合成属性结构以表示“部分-整体”的层次结构。组合使得用户和单个对象和组合对象的使用具有一致性。 
这里写图片描述

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

举个简单例子(树枝和叶子) 
1 Component

public abstract class Component
{
    protected String name;

    public Component(String name)
    {
        this.name = name;
    }

    protected abstract void add(Component component);
    protected abstract void remove(Component component);
    protected abstract void operation(int depth);
    protected abstract List<Component> getChildren();
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

2 Leaf(树叶)

public class Leaf extends Component
{
    public Leaf(String name)
    {
        super(name);
    }

    @Deprecated
    public void add(Component component) throws UnsupportedOperationException
    {
        throw new UnsupportedOperationException();
    }

    @Deprecated
    public void remove(Component component) throws UnsupportedOperationException
    {
        throw new UnsupportedOperationException();
    }

    @Override
    protected void operation(int depth){
        String temp = "";
        for(int i=0;i<depth;i++)
        {
            temp += "    ";
        }
        System.out.println(temp+this.name);
    }

    @Deprecated
    protected List<Component> getChildren() throws UnsupportedOperationException
    {
        throw new UnsupportedOperationException();
    }
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

3 Composite(树枝)

public class Composite extends Component
{
    private LinkedList<Component> children;

    public Composite(String name)
    {
        super(name);
        this.children = new LinkedList<>();
    }

    public void add(Component component)
    {
        this.children.add(component);
    }

    @Override
    public void remove(Component component)
    {
        this.children.remove(component);
    }

    @Override
    public LinkedList<Component> getChildren()
    {
        return children;
    }

    @Override
    protected void operation(int depth)
    {
        String temp = "";
        for(int i=0;i<depth;i++)
        {
            temp += "    ";
        }

        LinkedList<Component> children = this.getChildren();
        System.out.println(temp+this.name);
        for (Component c : children) {
            c.operation(depth+1);
        }
    }
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43

4 测试代码

public class MainTest
{
    public static void main(String[] args)
    {
        Composite root = new Composite("树根");

        Composite branch01 = new Composite("树枝01");
        Composite branch02 = new Composite("树枝02");
        Composite branch03 = new Composite("树枝03");
        Composite branch04 = new Composite("树枝04");

        branch01.add(new Leaf("树叶01"));
        branch01.add(new Leaf("树叶02"));
        branch03.add(new Leaf("树叶03"));
        branch03.add(new Leaf("树叶04"));
        branch03.add(new Leaf("树叶05"));
        branch01.add(branch03);

        branch02.add(new Leaf("树叶06"));
        branch02.add(new Leaf("树叶07"));
        branch02.add(new Leaf("树叶08"));
        branch04.add(new Leaf("树叶09"));
        branch04.add(new Leaf("树叶10"));
        branch02.add(branch04);

        root.add(branch01);
        root.add(branch02);

        root.operation(0);
    }
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

输出结果:

树根
    树枝01
        树叶01
        树叶02
        树枝03
            树叶03
            树叶04
            树叶05
    树枝02
        树叶06
        树叶07
        树叶08
        树枝04
            树叶09
            树叶10
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

上面测试代码部分构建树结构的代码着实不太好看,效率太低。其实,在真实应用中。并不是这样子手工地构建一棵复杂的树,应该是我们已经将整棵树的节点内容、逻辑关系都存储在数据库表中,开发人员编程从数据库中的表中读取记录来构建整棵树。

透明模式 vs 安全模式 
上面的代码属于透明模式,我们先看看安全模式是怎么实现的: 
更改一下Component:

public abstract class Component
{
    protected String name;

    public Component(String name)
    {
        this.name = name;
    }
    protected abstract void operation(int depth);
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

再更改下Leaf

public class Leaf extends Component
{
    public Leaf(String name)
    {
        super(name);
    }

    @Override
    protected void operation(int depth){
        String temp = "";
        for(int i=0;i<depth;i++)
        {
            temp += "    ";
        }
        System.out.println(temp+this.name);
    }
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

被称为安全模式是因为Leaf不具有add和remove等方法,这些具体方法是被下置到Composite类中去具体实现了。透明模式将add和remove等方法上升到抽象构建类Component中去了。那么此时Leaf类在具体实现时就必须将继承而来的add和remove等不可用、不合理的方法给注释掉(@Deprecated),并抛出适当的异常,不提供给用户使用。至于是使用透明模式还是安全模式就仁者见仁智者见智咯。不过,在这一模式中,相对于安全性,我们比较强调透明性。

使用场景

  1. 用于对象的部分-整体层次结构,如树形菜单、文件夹菜单、部门组织架构等。
  2. 对用户隐藏组合对象与单个对象的不同,使得用户统一地使用组合结构中的所有对象。

优缺点 
优点:使客户端调用简单,客户端可以一直的使用组合结构或其中单个对象,用户就不必关心自己处理的是单个对象还是整个组合结构,这就简化了客户端代码。更容易在组合体内加入对象不见,客户端不必因为加入了新的对象不见而更改代码。这一点符合开闭原则的要求,对系统的二次开发和功能扩展很有利。 
缺点:组合模式不容易限制组合中的构件。

Jdk中的组合模式 
java.util.Map#putAll(Map) 
java.util.List#addAll(Collection) 
java.util.Set#addAll(Collection)

反面教材 
 回想起以前写页面菜单的时候,从数据库中读取类似的树形结构,当时没有采用这种组合模式,而是限定死树的最大深度(页面的菜单深度不超过3,实际上也没见到过超过3的),这样前端js代码在深度不超过3时没有问题,当超过3时,就需要重新编写页面菜单的js代码了,这样的扩展性并不好,如果当时采用了这种组合模式就会好很多。 
 写页面菜单已经是6年前的事了,代码早已不详~最近用项目需求要写一棵树,单例模式的。大伙来看看有什么不妥之处。

首先定义实体类Entry

public class Entry
{
    private String name;
    //some other properties

    public Entry(String name)
    {
        super();
        this.name = name;
    }
    //getter and setter略
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

定义树节点(这里的叶子节点可以看成son==null的TreeNode,而树枝节点son!=null)

public class TreeNode
{
    private Entry entry;
    private List<TreeNode> son;
    //getter and setter略
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

定义单例树节点

public class Tree
{
    private TreeNode tree;

    private Tree()
    {
        tree = new TreeNode();
        tree.setEntry(null);
        tree.setSon(new ArrayList<TreeNode>());
    }

    public static class SingletonTreeInner
    {
        public static final Tree INSTANCE = new Tree();
    }

    public static Tree getInstance()
    {
        return SingletonTreeInner.INSTANCE;
    }

    public TreeNode getTree()
    {
        return tree;
    }
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

测试代码(根-集群-主机-虚拟机的层次关系):

    public static void insertData()
    {
        Tree root = Tree.getInstance();
        List<TreeNode> rootList = root.getTree().getSon();

        //add cluster into root node
        TreeNode cluster = new TreeNode();
        Entry entry1 = new Entry("cluster1");
        cluster.setEntry(entry1);
        cluster.setSon(null);
        rootList.add(cluster);

        //add host into cluster node
        TreeNode host = new TreeNode();
        Entry entry2 = new Entry("host1");
        host.setEntry(entry2);
        host.setSon(null);
        if(cluster.getSon() == null)
        {
            cluster.setSon(new ArrayList<TreeNode>());
        }
        List clusterList = cluster.getSon();
        clusterList.add(host);

        //add vm into host node
        TreeNode vm = new TreeNode();
        Entry entry3 = new Entry("vm1");
        vm.setEntry(entry3);
        vm.setSon(null);
        if(host.getSon() == null)
        {
            host.setSon(new ArrayList<TreeNode>());
        }
        List hostList = host.getSon();
        hostList.add(vm);
    }
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

大伙比对组合模式来看看这段代码有什么欠妥之处,欢迎在下方留言探讨。

参考资料 
1. 《23种设计模式》 
2. 《细数JDK里的设计模式》 
3. 《组合模式(Composite)的安全模式与透明模式

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值