一:组合模式
1.引出问题
无论java中还是其他软件设计中,都会有着这样一种结构-树形结构。比如遇到最多的就是地区(地区比较复杂其中不仅涉及到简单的树结构还涉及到数据比较深的左右结构的构成,在java项目中,地区的存储不是一个地区一个记录而是以上下左右节点去存储的,有兴趣的同学可以自行百度哦)等,这里我们举一个比较简单的示例。
2.引出概念
在上述的图示中,我们可以看出3种类型的节点。第一种只有子节点没有父节点(服装),第二种既有子节点又有父节点(男装/女装),第三种只有父节点没有子节点(最下面一层)。那么就会涉及到一种问题,所有有子节点的节点,在删除时要么允许全部删除包括其下的子节点,要么柚子节点不允许删除。这个问题稍后会讨论。
在java中有这么一种结构类型用来维护这种树结构,可以让用户容易的操作树结构--组合结构。结合上图 ,讲解一下组合结构中的一些角色问题。
根节点:
树节点:
叶节点: 只有父节点的。(第三种节点类型)
在上述的分析中我们可以抽象出组合机构的类图模型:
(1)抽象组件角色(Component):即根节点抽象出来的声明接口,为组合对象(树节点)和叶对象(叶节点)提供方方声明
(2)组合对象角色(Composite):即树节点的的存储以及树节点的行为组件的存储。
(3)叶子对象角色(Leaf):实现和定义叶对象的存储和行为的。
二:代码
1.抽象出组件声明
package test.com;
import java.nio.channels.UnsupportedAddressTypeException;
/**
* 更节点的抽象组件方法声明
* @author monxz
*
* @time 2019年7月19日
*/
public abstract class Component {
/**
* 树节点或者叶节点可能需要实现的方法,比如打印或者组合成其他的结构展示
* @param str
*/
public abstract void someOperation(String str);
/**
* 添加子节点
* @param child
*/
public void addChild(Component child) {
System.err.println("添加子节点");
// 缺省的实现,抛出异常,因为叶子对象没有这个功能,或子类未实现这个功能
throw new UnsupportedOperationException("对象未支持此方法");
}
/**
* 删除子节点
* @param child
*/
public void removeChild(Component child) {
System.err.println("删除子节点");
// 缺省的实现,抛出异常,因为叶子对象没有这个功能,或子类未实现这个功能
throw new UnsupportedOperationException("对象不支持此功能");
}
/**
* 获取子节点
* @param index
* @return
*/
public Component getChildren(int index) {
System.err.println("获取子节点");
// 缺省的实现,抛出异常,因为叶子对象没有这个功能,或子类未实现这个功能
throw new UnsupportedOperationException("对象不支持此功能");
}
}
2.抽象实现(有子节点的组件声明)
说明:这里很精妙的将有子节点的用户都做实现了。
package test.com;
import java.util.ArrayList;
import java.util.List;
/**
* 树节点
* @author monxz
*
* @time 2019年7月19日
*/
public class Composite extends Component{
//数组件的声明
private String name ="";
public Composite(String name) {
this.name=name;
}
//其他组件对象的声明
private List<Component> childComponents =null;
@Override
public void someOperation(String str) {
// 先把自己输出
System.out.println(str + "+" + name);
// 如果还包含其他子组件,那么就输出这些子组件对象
if (childComponents != null) {
// 添加一个空格,表示向后缩进一个空格
str += " ";
// 输出当前对象的子组件对象
for (Component component : childComponents) {
// 递归地进行子组件相应方法的调用,输出每个子组件对象
component.someOperation(str);
}
}
}
/**
* 添加子节点
*/
public void addChild(Component child) {
// 延迟初始化
if (null == childComponents) {
childComponents = new ArrayList<Component>();
}
childComponents.add(child);
}
/**
* 删除子节点
*/
public void removeChild(Component child) {
if (null != childComponents) {
childComponents.remove(child);
}
}
/**
* 根据下标获取到子节点
*/
public Component getChildren(int index) {
if (null != childComponents) {
if (index >= 0 && index < childComponents.size()) {
return childComponents.get(index);
}
}
return null;
}
}
3.叶节点的组件
package test.com;
/**
* 叶子的声明
* @author monxz
*
* @time 2019年7月19日
*/
public class Leaf extends Component {
private String name="";
public Leaf(String name) {
super();
this.name = name;
}
@Override
public void someOperation(String preStr) {
System.out.println(preStr + "-" + name);
}
}
4.测试
package test.com;
public class test {
public static void main(String[] args) {
//1.先定义一个更节点
Component root=new Composite("根菜单");
//2.一级菜单定义
Component a1 = new Composite("一级菜单1");
Component a2 = new Composite("一级菜单2");
Component a3 = new Leaf("一级菜单3");
//3.定义二级菜单
Component b11 = new Leaf("二级菜单1.1");
Component b12 = new Leaf("二级菜单1.2");
Component b13 = new Leaf("二级菜单1.3");
Component b21 = new Leaf("二级菜单2.1");
Component b22 = new Leaf("二级菜单2.2");
root.addChild(a1);
root.addChild(a2);
root.addChild(a3);
a1.addChild(b11);
a1.addChild(b12);
a1.addChild(b13);
a2.addChild(b21);
a2.addChild(b22);
root.someOperation("");
}
}
5.说明
使用递归的方式,实现一级一级菜单新增。这种方式在删除菜单式也会将子节点的所有菜单都会删掉。
3.总结
在组合模式中对于树结构有一种很好的维护。