在迭代器模式中往菜单中加入了迭代器,但是存在一个问题服务员在打印菜单时,每次增加一份菜单就要修改代码,违反开闭原则:
public void printMenu(){Iterator pancakeIterator = pancakeHouseMenu.createIterator();Iterator cafeIterator = cafeMenu.createIterator();printMenu(pancakeIterator);printMenu(cafeIterator); }
为什么不尝试将所有菜单放入一个集合中?
public class Waitress{ArrayList menus; // 统一到一个菜单集合中,该菜单集合包含多种菜单public Waitress(ArrayList menus){this.menus = menus;}public void printMenu(){// 在菜单集合上进行遍历Iterator menuIterator = menus.iterator();while(menuIterator.hasNext()){Menu menu = (Menu)menuIterator.next();printMenu(menu.createIterator());}}private void printMenu(Iterator iterator){while(iterator.hasNext()){MenuItem item = (Menuitem)iterator.next();// 打印操作,此处省略}} }
上述方法很好解决了问题,但是此时要在咖啡厅菜单中加入一个甜点菜单,这个甜点菜单是一份单独的菜单,并不是将甜点项(菜单项)添加到咖啡厅菜单中,该如何实现?
- 允许将对象组合为树形结构来表现整体/部分层次结构,树中包含了组合以及个别对象(即叶子节点和非叶子节点)
- 组合能让客户通过一致的方式处理个别对象和对象组合(即实现相同的接口)
菜单组件可以是某个菜单项(叶子节点),也可以是某个菜单(组合,也就是非叶子节点)
public abstract class MenuComponent{// 下面方法有些只对菜单项(叶子节点)有意义,有些只对菜单(组合,也就是非叶子节点)有意义,所以默认实现抛出异常public void add(MenuComponent menuComponent){ // 新增菜单组件throw new UnsupporteOperationException();}public void remove(MenuComponent menuComponent){ // 删除菜单组件throw new UnsupporteOperationException();}public MenuComponent getChild(int i){ // 获取菜单组件throw new UnsupporteOperationException();}// 还有getName、getPrice等方法,此处省略
}
public class MenuItem extends MenuComponent{String name;double price;public MenuItem(String name, double price){this.name = name;this.price = price;}// 还有getName、getPrice方法,此处省略public void print(){// 打印菜品名称和价格,此处省略}
}
对于组合菜单还可以有多个菜单项(叶子节点)以及其他菜单(即子树)
public class Menu extends MenuComponent{ArrayList menuComponents = new ArrayList(); // 可以有任意数目的孩子,但是都必须是MenuComponent类型String name;String desc;public MenuItem(String name, String desc){this.name = name;this.desc = desc;}public void add(MenuComponent menuComponent){menuComponents.add(menuComponent);}public void remove(MenuComponent menuComponent){menuComponents.remove(menuComponent);}public MenuComponent getChild(int i){return (MenuComponent)menuComponents.get(i);}// 还有getName、getDesc方法,此处省略public void print(){// 打印菜单名称和描述,此处省略// 除了菜单本身信息,还要打印出菜单中所有组件的信息(即其他菜单和菜单项)Iterator iterator = menuComponents.iterator();while(iterator.hasNext()){MenuComponent menuComponent = (MenuComponent)iterator.next();menuComponent.print(); // 递归调用}}
}
把最顶层的菜单组件(即整个树的根节点)给服务员
public class Waitress{MenuComponent rootmenu; // 将根节点的交给服务员即可public Waitress(MenuComponent rootmenu){this.rootmenu = rootmenu;}public void printMenu(){rootmenu.print();}
}
public class MenuTest{public static void main(String[] agrs){// 定义菜单组件(根节点也是菜单组件)MenuComponent cafeMenu = new Menu("CAFE MENU", "Dinner"); MenuComponent pancakeHouseMenu = new Menu("PNCAKE HOUSE MENU", "Breakfast");MenuComponent dessertMenu = new Menu("DESSRET MENU", "Anytime");MenuComponent rootMenu = new Menu("ALL MENUS", "All menus combined");// 开始构建树// 1.添加子树rootMenu.add(cafeMenu);rootMenu.add(pancakeHouseMenu);// 2.往子树中添加叶子节点或其他子树cafeMenu.add(dessertMenu); // 实现开始的目标:往咖啡厅菜单中添加甜点菜单cafeMenu.add(new MenuItem("BLACK COFFEE", 25)); // 咖啡厅肯定需要咖啡的// 把完整菜单给服务员Waitress waitress = new Waitress(rootMenu); waitress.printMenu();}
}
模式 | 描述 |
---|---|
适配器模式 | 改变一个或多个接口 |
迭代器模式 | 提供一个方式遍历集合,且无需暴露集合的实现 |
外观模式 | 简化一群类的接口 |
组合模式 | 客户可将对象的集合和个别对象一视同仁 |
观察者模式 | 当某个对象改变时,允许一群对象被通知 |
Head First 设计模式-组合模式
设计模式-组合模式