使用软件模拟大树的根节点和树枝节点和叶子节点
抽象为两类,容器节点和叶子节点
package demo14.composite.example1; import java.util.*; /** * 组合对象,可以包含其它组合对象或者叶子对象 */ 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); } } } *********************************************************************************************** package demo14.composite.example1; /** * 叶子对象 */ 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); } } *************************************************************************************************** package demo14.composite.example1; 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(""); } }
大家很容易就发现了一个问题:那就是必须区分叶子节点和容器节点,无论是客户端还是内部都要区分,那么这样就会使得程序变得复杂,对于功能的扩展也不方便,而且要知道两个类才行,那么我们能不能把他整合成一个类呢,让客户端如下显示呢?答案是可以的。
package demo14.composite.example2; 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); } }
很明显,上述客户端的代码就顺眼多了,那么我们改如何实现呢?
将对象组合成树形结构以表示“部分-整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。
package demo14.composite.example2; /** * 抽象的组件对象,为组合中的对象声明接口,实现接口的缺省行为 */ public abstract class Component { /** * 示意方法,子组件对象可能有的功能方法 */ public abstract void someOperation(); /** * 向组合对象中加入组件对象 * * @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("对象不支持这个功能"); } } ********************************************************************** package demo14.composite.example2; import java.util.*; /** * 组合对象,通常需要存储子对象,定义有子部件的部件行为, * 并实现在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; } } ********************************************************************** package demo14.composite.example2; /** * 叶子对象,叶子对象不再包含其它子对象 */ public class Leaf extends Component { /** * 示意方法,叶子对象可能有自己的功能方法 */ public void someOperation() { // do something } } ********************************************************************** package demo14.composite.example2; 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); } }
代码的修改量并不大,跟示例代码差不多
package demo14.composite.example3; /** * 抽象的组件对象 */ public abstract class Component { /** * 输出组件自身的名称 */ 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("对象不支持这个功能"); } } ********************************************************************************************* package demo14.composite.example3; import java.util.*; /** * 组合对象,可以包含其它组合对象或者叶子对象 */ public class Composite extends Component{ /** * 用来存储组合对象中包含的子组件对象 */ private List<Component> childComponents = null; /** * 组合对象的名字 */ private String name = ""; /** * 构造方法,传入组合对象的名字 * @param name 组合对象的名字 */ public Composite(String name){ this.name = name; } public void addChild(Component child) { //延迟初始化 if (childComponents == null) { childComponents = new ArrayList<Component>(); } childComponents.add(child); } /** * 输出组合对象自身的结构 * @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); } } } } ********************************************************************************************** package demo14.composite.example3; /** * 叶子对象 */ 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); } } ******************************************************************************************** package demo14.composite.example3; public class Client { public static void main(String[] args) { //定义所有的组合对象 Component root = new Composite("服装"); Component c1 = new Composite("男装"); Component c2 = new Composite("女装"); //定义所有的叶子对象 Component leaf1 = new Leaf("衬衣"); Component leaf2 = new Leaf("夹克"); Component leaf3 = new Leaf("裙子"); Component leaf4 = new Leaf("套装"); //按照树的结构来组合组合对象和叶子对象 root.addChild(c1); root.addChild(c2); c1.addChild(leaf1); c1.addChild(leaf2); c2.addChild(leaf3); c2.addChild(leaf4); //调用根对象的输出功能来输出整棵树 root.printStruct(""); } }
目的:客户端不再区分叶子节点和组合对象
关键点:设计一个抽象的组合对象
统一叶子对象和组合对象