研磨设计模式 之 组合模式(Composite) 1——跟着cc学设计系列

15.1  场景问题

15.1.1  商品类别树

       考虑这样一个实际的应用:管理商品类别树。

在实现跟商品有关的应用系统的时候,一个很常见的功能就是商品类别树的管理,比如有如下所示的商品类别树:

+服装

+男装

                                -衬衣

                                -夹克

                            +女装

                                -裙子

                                -套装

       仔细观察上面的商品类别树,有以下几个明显的特点:

  • 有一个根节点,比如服装,它没有父节点,它可以包含其它的节点
  • 树枝节点,有一类节点可以包含其它的节点,称之为树枝节点,比如男装、女装
  • 叶子节点,有一类节点没有子节点,称之为叶子节点,比如衬衣、夹克、裙子、套装

现在需要管理商品类别树,假如就要求能实现输出如上商品类别树的结构的功能,应该如何实现呢?

15.1.2  不用模式的解决方案

       要管理商品类别树,就是要管理树的各个节点,现在树上的节点有三类,根节点、树枝节点和叶子节点,再进一步分析发现,根节点和树枝节点是类似的,都是可以包含其它节点的节点,把它们称为容器节点。

       这样一来,商品类别树的节点就被分成了两种,一种是容器节点,另一种是叶子节点。容器节点可以包含其它的容器节点或者叶子节点。把它们分别实现成为对象,也就是容器对象和叶子对象,容器对象可以包含其它的容器对象或者叶子对象,换句话说,容器对象是一种组合对象。

然后在组合对象和叶子对象里面去实现要求的功能就可以了,看看代码实现。

(1)先看叶子对象的代码实现,示例代码如下:

/**

 * 叶子对象

 */

public class Leaf {

    /**

     * 叶子对象的名字

     */

    private String name = "";

 

    /**

     * 构造方法,传入叶子对象的名字

     * @param name 叶子对象的名字

     */

    public Leaf(String name){

       this.name = name;

    }

 

    /**

     * 输出叶子对象的结构,叶子对象没有子对象,也就是输出叶子对象的名字

     * @param preStr 前缀,主要是按照层级拼接的空格,实现向后缩进

     */

    public void printStruct(String preStr){

       System.out.println(preStr+"-"+name);

    }

}

(2)再来看看组合对象的代码实现,组合对象里面可以包含其它的组合对象或者是叶子对象,由于类型不一样,需要分开记录。示例代码如下:

/**

 * 组合对象,可以包含其它组合对象或者叶子对象

 */

public class Composite {

    /**

     * 用来记录包含的其它组合对象

     */

    private Collection childComposite =

new ArrayList();

    /**

     * 用来记录包含的其它叶子对象

     */

    private Collection childLeaf = new ArrayList();

    /**

     * 组合对象的名字

     */

    private String name = "";

 

    /**

     * 构造方法,传入组合对象的名字

     * @param name 组合对象的名字

     */

    public Composite(String name){

       this.name = name;

    }

 

    /**

     * 向组合对象加入被它包含的其它组合对象

     * @param c 被它包含的其它组合对象

     */

    public void addComposite(Composite c){

       this.childComposite.add(c);

    }

    /**

     * 向组合对象加入被它包含的叶子对象

     * @param leaf 被它包含的叶子对象

     */

    public void addLeaf(Leaf leaf){

       this.childLeaf.add(leaf);

    }

    /**

     * 输出组合对象自身的结构

     * @param preStr 前缀,主要是按照层级拼接的空格,实现向后缩进

     */

    public void printStruct(String preStr){

       //先把自己输出去

       System.out.println(preStr+"+"+this.name);

       //然后添加一个空格,表示向后缩进一个空格,输出自己包含的叶子对象

       preStr+=" ";

       for(Leaf leaf : childLeaf){

           leaf.printStruct(preStr);

       }

       //输出当前对象的子对象了

       for(Composite c : childComposite){

           //递归输出每个子对象

           c.printStruct(preStr);

       }

    }

}

(3)写个客户端来测试一下,看看是否能实现要求的功能,示例代码如下:

public class Client {

    public static void main(String[] args) {

       //定义所有的组合对象

       Composite root = new Composite("服装");

       Composite c1 = new Composite("男装");

       Composite c2 = new Composite("女装");

 

       //定义所有的叶子对象

       Leaf leaf1 = new Leaf("衬衣");

       Leaf leaf2 = new Leaf("夹克");

       Leaf leaf3 = new Leaf("裙子");

       Leaf leaf4 = new Leaf("套装");

 

       //按照树的结构来组合组合对象和叶子对象

       root.addComposite(c1);

       root.addComposite(c2);     

       c1.addLeaf(leaf1);

       c1.addLeaf(leaf2);      

       c2.addLeaf(leaf3);

       c2.addLeaf(leaf4);      

 

       //调用根对象的输出功能来输出整棵树

       root.printStruct("");

    }

}

运行一下,测试看看,是否能完成要求的功能。

15.1.3  有何问题

上面的实现,虽然能实现要求的功能,但是有一个很明显的问题:那就是必须区分组合对象和叶子对象,并进行有区别的对待,比如在Composite和Client里面,都需要去区别对待这两种对象。

区别对待组合对象和叶子对象,不仅让程序变得复杂,还对功能的扩展也带来不便。实际上,大多数情况下用户并不想要去区别它们,而是认为它们是一样的,这样他们操作起来最简单。

换句话说,对于这种具有整体与部分关系,并能组合成树形结构的对象结构,如何才能够以一个统一的方式来进行操作呢?

 

---------------------------------------------------------------------------

私塾在线学习网原创内容  跟着cc学设计系列 之 研磨设计模式

研磨设计讨论群【252780326】

原创内容,转载请注明出处【http://sishuok.com/forum/blogPost/list/5511.html】

---------------------------------------------------------------------------

你可能感兴趣的:(研磨设计模式,设计模式)