Head First设计模式(阅读笔记)-11.组合模式

继续修改菜单

迭代器模式中往菜单中加入了迭代器,但是存在一个问题服务员在打印菜单时,每次增加一份菜单就要修改代码,违反开闭原则:

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 设计模式-组合模式

设计模式-组合模式

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值