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<Composite> childComposite = new ArrayList<Composite>(); /** * 用来记录包含的其它叶子对象 */ private Collection<Leaf> childLeaf = new ArrayList<Leaf>(); /** * 组合对象的名字 */ 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】
---------------------------------------------------------------------------