HeadFirst设计模式学习之组合模式
组合模式定义
允许你将对象组合成树形结构来表现“整体/部分”层次结构。组合能让客户以一致的方式处理个别对
象以及对象组合
从定义出发可以得出两个结论
- 组合模式是基于树这种数据结构的
- 组合模式的目的是“用一致的方式处理个别对象以及对象组合”
需求
设计菜单,菜单能包含菜单项或者子菜单,子菜单能包含菜单项或者二级子菜单,以此类推。
分析
- 菜单和菜单项应拥有共同的基类
- 基类应该拥有菜单和菜单项的所有方法(如果不这样,我们将无法“用一致的方式处理个别对象以及对象组合”)
- 迭代器才是正确的遍历方法。
设计基类MenuComponent
abstract class MenuComponent {
public void add(MenuComponent mc)
{
throw new UnsupportedOperationException();
}
public void remove(MenuComponent mc)
{
throw new UnsupportedOperationException();
}
public String getDescription()
{
throw new UnsupportedOperationException();
}
public void print()
{
throw new UnsupportedOperationException();
}
}
在分析中提到,基类应该拥有菜单和菜单项的所有方法,但是这样会造成的问题是,菜单项会具有其本不该有的方法,如add(),所以在基类里做了抛出异常的实现,这样,菜单和菜单项只需要复写需要的方法,用户调用不适当的方法会抛出异常
菜单类Menu
class Menu extends MenuComponent {
private String description;
private ArrayList<MenuComponent> al;
public Menu(String desc) {
// TODO Auto-generated constructor stub
this.description=desc;
al=new ArrayList<MenuComponent>();
}
@Override
public void add(MenuComponent mc) {
// TODO Auto-generated method stub
al.add(mc);
}
@Override
public void remove(MenuComponent mc) {
// TODO Auto-generated method stub
al.remove(mc);
}
@Override
public String getDescription() {
// TODO Auto-generated method stub
return description;
}
@Override
public void print() {
// TODO Auto-generated method stub
System.out.println(getDescription());
MyIterator MI=createIterator();
while(MI.hasNext())
{
MenuComponent mc=(MenuComponent)MI.next();
mc.print();
}
}
public MyIterator createIterator()
{
return new arrayDataIterator(al);
}
}
在这个菜单类里,我们复写了所有的方法并且添加了用于实现迭代器的方法createIterator(),这里的
arrayDataIterator类是在我的上一篇博客——迭代器模式中实现的用于遍历ArrayList的简单迭代器。
在方法print()中,是用迭代器实现的对菜单项的遍历和递归,这也解释了为什么要用迭代器:如果不用,递归会变得十分复杂(并非所有的菜单都一定会用ArrayList来存储菜单项)。
菜单项MenuItem
class MenuItem extends MenuComponent {
private String description;
public MenuItem(String desc) {
// TODO Auto-generated constructor stub
this.description=desc;
}
@Override
public String getDescription() {
// TODO Auto-generated method stub
return description;
}
@Override
public void print() {
// TODO Auto-generated method stub
System.out.println(getDescription());
}
}
在这里,我们只需要实现两个方法:用于得到描述的getDescription()和打印信息的print()。
测试
先放上测试代码:
public static void main(String[] args)
{
Menu menu = new Menu("菜单");
Menu elseMenu = new Menu("其他");
MenuItem item1 = new MenuItem("设置");
MenuItem item2 = new MenuItem("其他选项1");
MenuItem item3 = new MenuItem("其他选项2");
menu.add(elseMenu);
menu.add(item1);
elseMenu.add(item3);
elseMenu.add(item2);
menu.print();
}
上述代码,菜单包含菜单项设置和子菜单其他,其他子菜单包含两个菜单项:其他选项1和其他选项2,最后打印菜单信息。
测试结果
菜单
其他
其他选项2
其他选项1
设置
组合模式是为了用一致的方式处理个别对象以及对象组合,但是仅仅用组合模式并不如想象般美好——存储对象的数据结构可能不止一种,这就带来了复杂的递归操作(最糟糕的是每增加一种,都需要对递归进行相应的改变),但是迭代器可以将这个致命的缺点完美解决了,迭代器使组合的递归变得简单并且——也是最重要的——增加数据结构,只需要增加迭代器而不用修改代码!