《HF 设计模式》 C9 迭代器模式&组合模式

1 迭代器模式

1.1 来自餐厅的需求

现在有两家店要进行合并,一家是早餐店,一家是午餐店,他们有着各自的菜单,各自的招待员,每个菜单项记录:菜名,简介,是否为素食,价格

/**
 * @author 雫
 * @date 2021/3/9 - 11:35
 * @function 菜单项
 */
public class MenuItem {
    private String name;
    private String description;
    private boolean vegetarian;
    private double price;

    public MenuItem(String name, String description, boolean vegetarian, double price) {
        this.name = name;
        this.description = description;
        this.vegetarian = vegetarian;
        this.price = price;
    }

    public String getName() {
        return name;
    }

    public String getDescription() {
        return description;
    }

    public boolean isVegetarian() {
        return vegetarian;
    }

    public double getPrice() {
        return price;
    }
    
}

这两家店采用相同的MenuItem,但是问题来了,他们采用了不同的集合,早餐店老板希望以后能扩展他的菜单,采用了ArrayList来封装MenuItem,午餐店老板希望能控制菜单的长度,采用了数组来封装MenuItem:

/**
 * @author 雫
 * @date 2021/3/9 - 11:39
 * @function 早餐店的菜单
 * 采用ArrayList封装MenuItem
 */
public class AMenu {
    private ArrayList<MenuItem> menuItems;

    public AMenu() {
        this.menuItems = new ArrayList<>();

        addItem("bread", "good bread", true, 7);
        addItem("milk", "nice milk", true, 6);
        addItem("burger", "good chickenBurger", false, 10);
    }

    public void addItem(String name, String description, boolean vegetarian, double price) {
        MenuItem menuItem = new MenuItem(name, description, vegetarian, price);
        menuItems.add(menuItem);
    }

    public ArrayList<MenuItem> getMenuItems() {
        return this.menuItems;
    }

}

/**
 * @author 雫
 * @date 2021/3/9 - 11:44
 * @function 午餐店菜单
 * 采用数组封装MenuItem
 */
public class BMenu {
    private static final int MAX_ITEMS = 6;
    private int numberOfItems = 0;
    private MenuItem[] menuItems;

    public BMenu() {
        menuItems = new MenuItem[MAX_ITEMS];

        addItem("soup", "good soup", true, 20);
        addItem("chicken", "nice chicken", false, 40);
        addItem("juice", "good juice", true, 15);
    }

    public void addItem(String name, String description, boolean vegetarian, double price) {
        MenuItem menuItem = new MenuItem(name, description, vegetarian, price);
        if(this.numberOfItems >= MAX_ITEMS) {
            System.out.println("菜单已满");
        } else {
            menuItems[numberOfItems] = menuItem;
            numberOfItems = numberOfItems + 1;
        }
    }

    public MenuItem[] getMenuItems() {
        return this.menuItems;
    }

}

原先早餐店和午餐店各自经营,各有一个招待员,他们只用管理自己的列表/数组:

/**
 * @author 雫
 * @date 2021/3/9 - 12:04
 * @function 早餐店招待员
 */
public class AWaiter {
    private ArrayList<MenuItem> menuItems;

    public AWaiter(ArrayList<MenuItem> menuItems) {
        this.menuItems = menuItems;
    }

    public void printMenu() {
        for(MenuItem m : menuItems) {
            System.out.println(m.getName());
            System.out.println(m.getDescription());
            System.out.println(m.isVegetarian());
            System.out.println(m.getPrice());
        }
    }

    public void printVegetarian() {
        for(MenuItem m : menuItems) {
            if(m.isVegetarian()) {
                System.out.println(m.getName());
                System.out.println(m.getDescription());
                System.out.println(m.isVegetarian());
                System.out.println(m.getPrice());
            }
        }
    }

    public boolean isItemVegetarian(String name) {
        for(MenuItem m : menuItems) {
            if(name.equals(m.getName())) {
                if(m.isVegetarian()) {
                    return true;
                }
                return false;
            }
        }
        return false;
    }


}

现在我们需要给合并后的餐厅设计一个招待员,用于管理两个集合,要求他能够实现如下功能:

1,打印出两个菜单上的每一项
2,只打印早餐店的菜单项
3,只打印午餐店的菜单项
4,打印所有的素食菜单项
5,指定菜名,如果是素食返回true,否则返回false

1.2 粗糙地设计一个招待员

我们先从功能1开始,粗糙地设计一个招待员,打印两个菜单上的所有项:

/**
 * @author 雫
 * @date 2021/3/9 - 12:21
 * @function 合并后店铺的招待员
 */
public class Waiter {
    private ArrayList<MenuItem> AmenuItems;
    private MenuItem[] BmenuItems;

    public Waiter(ArrayList<MenuItem> amenuItems, MenuItem[] bmenuItems) {
        this.AmenuItems = amenuItems;
        this.BmenuItems = bmenuItems;
    }

    public void print() {
        for(int i = 0; i < AmenuItems.size(); i++) {
            MenuItem m = AmenuItems.get(i);
            System.out.println(m.getName() + ", " + m.getDescription()
                    + ", " + m.isVegetarian() + ", " + m.getPrice());
        }

        for(int i = 0; i < this.BmenuItems.length; i++) {
            MenuItem m = BmenuItems[i];
            System.out.println(m.getName() + ", " + m.getDescription()
                    + ", " + m.isVegetarian() + ", " + m.getPrice());
        }
    }

    
}

其它的功能若要实现,与上述的实现方式类似,都必须对两个菜单进行遍历处理,如果需要增加第三个菜单,就得遍历3次,以此类推

上述粗糙设计的招待员,存在如下问题:
1,针对实现编程,将功能和实现捆绑到一起

2,如果要增加新的菜单,就必须大面积更改源码,违反了开闭原则

3,招待员想要实现功能,就必须知道各个菜单集合的管理方式,违反了封装

并且早餐店和晚餐店的店主都不愿意更改自己的代码,那么上述实现的招待员变得难以维护,缺乏弹性,我们需要一种新的方式来管理不同的集合

1.3 创建自己的迭代器

回顾刚才遍历列表和数组的过程:
在这里插入图片描述
想要遍历列表,需要用到size()方法,想要遍历数组,需要用到length()方法,对于不同的集合需要不同的方法来遍历

现在我们创建一个接口,称为迭代器,其中的两个抽象方法用于遍历集合,为每种集合创建一个类实现该迭代器:

/**
 * @author 雫
 * @date 2021/3/9 - 13:09
 * @function 迭代器接口
 */
public interface Iterator {

    boolean hasNext();

    Object next();
}


/**
 * @author 雫
 * @date 2021/3/9 - 13:10
 * @function 早餐店菜单迭代器
 */
public class AMenuIterator implements Iterator {

    private ArrayList<MenuItem> AMenuItems;
    private int position = 0;

    public AMenuIterator(ArrayList<MenuItem> AMenuItems) {
        this.AMenuItems = AMenuItems;
    }

    @Override
    public boolean hasNext() {
        if(this.position >= AMenuItems.size() || this.AMenuItems.get(this.position) == null) {
            return false;
        } else {
            return true;
        }
    }

    @Override
    public Object next() {
        MenuItem menuItem = AMenuItems.get(this.position);
        this.position = this.position + 1;
        return menuItem;
    }

}

/**
 * @author 雫
 * @date 2021/3/9 - 14:13
 * @function 午餐店菜单迭代器
 */
public class BMenuIterator implements Iterator {
    private MenuItem[] menuItems;
    private int position;

    public BMenuIterator(MenuItem[] menuItems) {
        this.menuItems = menuItems;
    }

    @Override
    public boolean hasNext() {
        if(this.position >= menuItems.length || menuItems[this.position] == null) {
            return false;
        } else {
            return true;
        }
    }

    @Override
    public Object next() {
        MenuItem menuItem = menuItems[this.position];
        this.position = this.position + 1;
        return menuItem;
    }

}

更改后的早餐店和午餐店:

/**
 * @author 雫
 * @date 2021/3/9 - 11:39
 * @function 早餐店的菜单
 * 采用ArrayList封装MenuItem
 */
public class AMenu {
    private ArrayList<MenuItem> menuItems;

    public AMenu() {
        this.menuItems = new ArrayList<>();

        addItem("bread", "good bread", true, 7);
        addItem("milk", "nice milk", true, 6);
        addItem("burger", "good chickenBurger", false, 10);
    }

    public void addItem(String name, String description, boolean vegetarian, double price) {
        MenuItem menuItem = new MenuItem(name, description, vegetarian, price);
        menuItems.add(menuItem);
    }

    public Iterator createIterator() {
        return new AMenuIterator(this.menuItems);
    }

}


/**
 * @author 雫
 * @date 2021/3/9 - 11:44
 * @function 午餐店菜单
 * 采用数组封装MenuItem
 */
public class BMenu {
    private static final int MAX_ITEMS = 3;
    private int numberOfItems = 0;
    private MenuItem[] menuItems;

    public BMenu() {
        menuItems = new MenuItem[MAX_ITEMS];

        addItem("soup", "good soup", true, 20);
        addItem("chicken", "nice chicken", false, 40);
        addItem("juice", "good juice", true, 15);
    }

    public void addItem(String name, String description, boolean vegetarian, double price) {
        MenuItem menuItem = new MenuItem(name, description, vegetarian, price);
        if(this.numberOfItems >= MAX_ITEMS) {
            System.out.println("菜单已满");
        } else {
            menuItems[numberOfItems] = menuItem;
            numberOfItems = numberOfItems + 1;
        }
    }

    public Iterator createIterator() {
        return new BMenuIterator(this.menuItems);
    }
    
}

更改后的招待:

/**
 * @author 雫
 * @date 2021/3/9 - 12:21
 * @function 使用迭代器的招待员
 */
public class Waiter {
    private AMenu aMenu;
    private BMenu bMenu;

    public Waiter(AMenu aMenu, BMenu bMenu) {
        this.aMenu = aMenu;
        this.bMenu = bMenu;
    }

    public void printAll() {
        Iterator aIterator = this.aMenu.createIterator();
        Iterator bIterator = this.bMenu.createIterator();
        printMenu(aIterator);
        printMenu(bIterator);
    }

    public void printMenu(Iterator iterator) {
        while (iterator.hasNext()) {
            MenuItem menuItem = (MenuItem) iterator.next();
            System.out.println(menuItem.getName() + ", " + menuItem.getDescription()
                    + ", " + menuItem.isVegetarian() + ", " + menuItem.getPrice());
        }
    }

}

测试:
在这里插入图片描述
使用迭代器设计的招待员特点:

1,菜单的实现被封装了起来,招待员并不知道菜单集合地实现

2,只需要一个循环,就可以多态地处理不同集合

但现在招待员依赖两个菜单对象,我们需要更进一步地设计,采用依赖倒置原则,将招待员解耦出来,让系统更具有弹性

1.4 使用java.util.Iterator

Java中有一个Iterator接口,就是专门用来为集合创建具体的迭代器:
在这里插入图片描述
除了hasNext()和next()方法外,还有一个remove()方法允许我们从集合中删除由next()方法返回的元素

remove()方法可有可无,但如果提供了remove()方法,要注意多线程环境下的使用

我们用java.util.Iterator来重新设计上面的代码:
由于ArrayList类有一个iterator方法,所以只需要为午餐店的数组集合创建一个迭代器即可

/**
 * @author 雫
 * @date 2021/3/9 - 15:02
 * @function 菜单接口
 */
public interface Menu {
    Iterator createIterator();
}


/**
 * @author 雫
 * @date 2021/3/9 - 14:56
 * @function 午餐店菜单迭代器
 */
public class BMenuIterator implements Iterator {
    private MenuItem[] menuItems;
    private int index;

    public BMenuIterator(MenuItem[] menuItems) {
        this.menuItems = menuItems;
        this.index = 0;
    }

    @Override
    public boolean hasNext() {
        if(index >= menuItems.length || menuItems[index] == null) {
            return false;
        } else {
            return true;
        }
    }

    @Override
    public Object next() {
        MenuItem menuItem = menuItems[index];
        index++;
        return menuItem;
    }

}


/**
 * @author 雫
 * @date 2021/3/9 - 14:58
 * @function
 */
public class BMenu implements Menu {

    private static final int MAX_ITEMS = 3;
    private int numberOfItems = 0;
    private MenuItem[] menuItems;

    public BMenu() {
        menuItems = new MenuItem[MAX_ITEMS];

        addItem("soup", "good soup", true, 20);
        addItem("chicken", "nice chicken", false, 40);
        addItem("juice", "good juice", true, 15);
    }

    public void addItem(String name, String description, boolean vegetarian, double price) {
        MenuItem menuItem = new MenuItem(name, description, vegetarian, price);
        if(this.numberOfItems >= MAX_ITEMS) {
            System.out.println("菜单已满");
        } else {
            menuItems[numberOfItems] = menuItem;
            numberOfItems = numberOfItems + 1;
        }
    }

    @Override
    public Iterator createIterator() {
        return new BMenuIterator(this.menuItems);
    }

}

经过重新设计后,Waiter类可以使用各种集合的迭代器,从而完成对集合的遍历操作,但Waite并不知道集合的具体实现,并与各种菜单类完成了解耦

1.5 定义迭代器模式

迭代器模式
提供一种方法顺序访问一个集合中的各个元素,且不暴露其内部的实现

对于一个拥有集合的类,我们应该为这个集合设计专用的迭代器,来负责该集合的遍历工作,减轻拥有集合的类的工作量,并同时为该类增加一个获取迭代器的方法
在这里插入图片描述

关于迭代器模式,最重要的就是迭代器接口:

/**
 * @author 雫
 * @date 2021/3/9 - 13:09
 * @function 迭代器接口
 */
public interface Iterator {

    boolean hasNext();

    Object next();
}

hasNext()将用来判断集合中是否还有下一个元素
next()将用来取得这个集合中的下一个元素

一旦有了这个Iterator接口,就可以为各种集合实现迭代器,如数组,列表,散列表等,之后就可以从具体的迭代器中遍历元素,从而屏蔽了集合的具体实现并且能够写出“多态的代码”

1.6 单一责任原则

设计原则

一个类应该只有一个引起变化的原因

当我们允许一个类不但要完成自己的任务时(管理某种集合),还同时要担负别的责任(遍历集合)时,我们就给了这个类两个变化的原因

为什么是两个?不是只有一个集合吗?
因为当这个集合改变时,这个类就需要改变,当遍历的方式改变时,这个类也需要改变,容易改变的必然不是坚固的

如果一个类有两个或多个职责,那么这会使得该类改变的变化率上升,这个类就变得脆弱,不易维护,应该尽量让每个类只有一种职责,将不同种类的功能分离

内聚:用来衡量一个类或模块的紧密程度
当一个模块或类被设计成只支持一组相关的功能时,称它高内聚,当被设计成支持一组不相关的功能时,称它低内聚

遵守 单一责任原则 的类/模块,更为“高内聚”且更易维护,我们需要高内聚,低耦合的代码

1.7 合并咖啡厅

现在我们的餐厅继续合并咖啡厅,咖啡厅也有它的菜单,菜单项仍然是MenuItem,来看看合并前的咖啡厅菜单:

/**
 * @author 雫
 * @date 2021/3/10 - 11:35
 * @function 咖啡厅菜单
 */
public class CMenu {
    private Hashtable<String, MenuItem> menuItems;

    public CMenu() {
        this.menuItems = new Hashtable();

        addItem("coffee", "good coffee", true, 30);
        addItem("cake", "nice cake", true, 20);
        addItem("hotDog", "good hotDog", false, 15);
    }

    public void addItem(String name, String description, boolean vegetarian, double price) {
        MenuItem menuItem = new MenuItem(name, description, vegetarian, price);
        menuItems.put(menuItem.getName(), menuItem);
    }

    public Hashtable<String, MenuItem> getItems() {
        return this.menuItems;
    }
    
}

现在我们修改咖啡厅的菜单,让它合并到先前的菜单且让招待员能够通过迭代器来遍历,为此我们需要创建一个能返回Hashtable迭代器的方法:

/**
 * @author 雫
 * @date 2021/3/10 - 11:35
 * @function 咖啡厅菜单
 */
public class CMenu implements Menu {
    private Hashtable<String, MenuItem> menuItems;

    public CMenu() {
        this.menuItems = new Hashtable();

        addItem("coffee", "good coffee", true, 30);
        addItem("cake", "nice cake", true, 20);
        addItem("hotDog", "good hotDog", false, 15);
    }

    public void addItem(String name, String description, boolean vegetarian, double price) {
        MenuItem menuItem = new MenuItem(name, description, vegetarian, price);
        menuItems.put(menuItem.getName(), menuItem);
    }
    
    @Override
    public Iterator createIterator() {
    //这种方式可以直接获得Hashtable的迭代器
        return menuItems.values().iterator();
    }
}

更改好咖啡厅的菜单后,我们将其加入到招待员类中:

/**
 * @author 雫
 * @date 2021/3/9 - 15:04
 * @function 招待员
 */
public class Waiter {
    private Menu aMenu;
    private Menu bMenu;
    private Menu cMenu;

    public Waiter(Menu aMenu, Menu bMenu, Menu cMenu) {
        this.aMenu = aMenu;
        this.bMenu = bMenu;
        this.cMenu = cMenu;
    }

    public void printAll() {
        Iterator aIterator = this.aMenu.createIterator();
        Iterator bIterator = this.bMenu.createIterator();
        Iterator cIterator = this.cMenu.createIterator();
        printMenu(aIterator);
        printMenu(bIterator);
        printMenu(cIterator);
    }

    public void printMenu(Iterator iterator) {
        while (iterator.hasNext()) {
            MenuItem menuItem = (MenuItem) iterator.next();
            System.out.println(menuItem.getName() + ", " + menuItem.getDescription()
                    + ", " + menuItem.isVegetarian() + ", " + menuItem.getPrice());
        }
    }

}

通过为集合创建迭代器的方式来统一遍历它们,让代码符合单一职责原则,且让遍历集合的类完成了解耦

1.8 迭代器与集合

所有的集合,如ArrayList,Vector,LinkedList,Stack等,这些类都实现了java.util.Collection这个接口,这个接口包含了很多有用的方法,可以操作一群对象

在这里插入图片描述
Collection接口中有add(),addAll(),clear(),contains(),equals(),isEmpty(),remove(),size(),iterator()等方法,这些方法在各种具体集合中被重写,以供我们使用,利用iterator方法就可以直接取得该集合的迭代器

Collection和Iterator的好处在于,每个Collection都知道如何创建自己的Iterator,只要调用ArrayList上的iterator(),就可以返回一个具体的Iterator

1.9 优化招待员

我们对比一下招待员在增加遍历咖啡厅菜单功能前后的代码:

/**
 * @author 雫
 * @date 2021/3/9 - 12:21
 * @function 使用迭代器的招待员
 * 遍历早餐店和午餐店菜单集合
 */
public class Waiter {
    private AMenu aMenu;
    private BMenu bMenu;

    public Waiter(AMenu aMenu, BMenu bMenu) {
        this.aMenu = aMenu;
        this.bMenu = bMenu;
    }

    public void printAll() {
        Iterator aIterator = this.aMenu.createIterator();
        Iterator bIterator = this.bMenu.createIterator();
        printMenu(aIterator);
        printMenu(bIterator);
    }

    public void printMenu(Iterator iterator) {
        while (iterator.hasNext()) {
            MenuItem menuItem = (MenuItem) iterator.next();
            System.out.println(menuItem.getName() + ", " + menuItem.getDescription()
                    + ", " + menuItem.isVegetarian() + ", " + menuItem.getPrice());
        }
    }

}


/**
 * @author 雫
 * @date 2021/3/9 - 15:04
 * @function 招待员
 * 遍历早餐店,午餐店,咖啡厅菜单集合
 */
public class Waiter {
    private Menu aMenu;
    private Menu bMenu;
    private Menu cMenu;

    public Waiter(Menu aMenu, Menu bMenu, Menu cMenu) {
        this.aMenu = aMenu;
        this.bMenu = bMenu;
        this.cMenu = cMenu;
    }

    public void printAll() {
        Iterator aIterator = this.aMenu.createIterator();
        Iterator bIterator = this.bMenu.createIterator();
        Iterator cIterator = this.cMenu.createIterator();
        printMenu(aIterator);
        printMenu(bIterator);
        printMenu(cIterator);
    }

    public void printMenu(Iterator iterator) {
        while (iterator.hasNext()) {
            MenuItem menuItem = (MenuItem) iterator.next();
            System.out.println(menuItem.getName() + ", " + menuItem.getDescription()
                    + ", " + menuItem.isVegetarian() + ", " + menuItem.getPrice());
        }
    }

}

差别很明显,当我们需要遍历新的菜单时,就需要向招待员中增加新的成员,并且更改构造方法,这又违反了开闭原则,每当我们需要增加/删除一种菜单时,就必须修改源码,我们需要将菜单封装起来,一起管理它们

为了统一管理这些菜单,可以将所有的菜单存入ArrayList中,然后遍历这个ArrayList中,依次取出它们的迭代器即可:

/**
 * @author 雫
 * @date 2021/3/9 - 15:04
 * @function 招待员
 * 遍历早餐店,午餐店,咖啡厅菜单集合
 */
public class Waiter {
    private ArrayList<Menu> menus;

    public Waiter(ArrayList<Menu> menus) {
        this.menus = menus;
    }

    public void printAll() {
        Iterator menuIterator = this.menus.iterator();
        while(menuIterator.hasNext()) {
            Menu menu = (Menu) menuIterator.next();
            printMenu(menu.createIterator());
        }
    }

    public void printMenu(Iterator iterator) {
        while (iterator.hasNext()) {
            MenuItem menuItem = (MenuItem) iterator.next();
            System.out.println(menuItem.getName() + ", " + menuItem.getDescription()
                    + ", " + menuItem.isVegetarian() + ", " + menuItem.getPrice());
        }
    }

}

这样使得代码符合了开闭原则,更易维护和扩展

1.10 新的问题…

现在无论再合并多少家店,都可以将其菜单加入到招待员存储菜单的ArrayList中,获取其迭代器进行遍历,但是如果原先的菜单发生了变化呢?

如现在午餐店想要加上一个甜点菜单,这个甜点菜单从属于午餐店的菜单,我们现在的代码无法将该甜点菜单加入到招待员管理的菜单中…
在这里插入图片描述
我们需要重构代码,以满足更多的需求…

2 组合模式

2.1 定义组合模式

为了增加子菜单,以及未来可能出现的子菜单的子菜单等情况,我们需要某种树形结构,可以容纳菜单,子菜单和菜单项,我们需要在每个菜单及其子菜单之间游走,以遍历任何存在于树形结构中的菜单

在这里插入图片描述
组合模式
允许将对象组合成树形结构来表现“整体/部分”的层次结构,组合能让客户以一致的方式来处理个别对象以及组合对象

在这里插入图片描述

即我们将菜单项和菜单全部当作“组件”,菜单项是叶子没有组件,菜单是节点,可以有组件,这样构成一个树形结构,无论是菜单项还是菜单,都是组件,用户可以通过控制组件来控制树形结构中的所有元素

2.2 利用组合模式设计菜单

在这里插入图片描述

所有组件的超类:

/**
 * @author 雫
 * @date 2021/3/10 - 12:49
 * @function 菜单和菜单项的抽象超类
 */
public abstract class MenuComponent {

    public void add(MenuComponent menuComponent) {
        throw new UnsupportedOperationException();
    }

    public void remove(MenuComponent menuComponent) {
        throw new UnsupportedOperationException();
    }

    public MenuComponent getChild(int i) {
        throw new UnsupportedOperationException();
    }
    
    public String getName() {
        throw new UnsupportedOperationException();
    }

    public String getDescription() {
        throw new UnsupportedOperationException();
    }

    public boolean isVegetarian() {
        throw new UnsupportedOperationException();
    }

    public double getPrice() {
        throw new UnsupportedOperationException();
    }

    public void print() {
        throw new UnsupportedOperationException();
    }

}

菜单项(叶子):

/**
 * @author 雫
 * @date 2021/3/10 - 12:56
 * @function 菜单项
 */
public class MenuItem extends MenuComponent{
    private String name;
    private String description;
    private boolean vegetarian;
    private double price;

    public MenuItem(String name, String description, boolean vegetarian, double price) {
        this.name = name;
        this.description = description;
        this.vegetarian = vegetarian;
        this.price = price;
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public String getDescription() {
        return description;
    }

    @Override
    public boolean isVegetarian() {
        return vegetarian;
    }

    @Override
    public double getPrice() {
        return price;
    }

    @Override
    public void print() {
        System.out.println(getName());
        System.out.println(getDescription());
        if(isVegetarian()) {
            System.out.println("Y");
        } else {
            System.out.println("N");
        }
        System.out.println(getPrice());
    }

}

菜单(节点):

/**
 * @author 雫
 * @date 2021/3/10 - 13:04
 * @function 菜单
 */
public class Menu extends MenuComponent {
    private ArrayList<MenuComponent> menuComponents;
    private String name;
    private String description;

    public Menu(String name, String description) {
        this.name = name;
        this.description = description;
    }

    @Override
    public void add(MenuComponent menuComponent) {
        menuComponents.add(menuComponent);
    }

    @Override
    public void remove(MenuComponent menuComponent) {
        menuComponents.remove(menuComponent);
    }

    @Override
    public MenuComponent getChild(int i) {
        return menuComponents.get(i);
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public String getDescription() {
        return description;
    }

    @Override
    public void print() {
        System.out.println(getName());
        System.out.println(getDescription());
        System.out.println("-------");

        Iterator iterator = menuComponents.iterator();
        while (iterator.hasNext()) {
            MenuComponent menuComponent = (MenuComponent) iterator.next();
            menuComponent.print();
        }
    }
    
}

招待员:

/**
 * @author 雫
 * @date 2021/3/10 - 13:21
 * @function 招待员
 */
public class Waiter {
    private MenuComponent allMenus;

    public Waiter(MenuComponent allMenus) {
        this.allMenus = allMenus;
    }
    
    public void printMenu() {
        allMenus.print();
    }
    
}

测试:

/**
 * @author 雫
 * @date 2021/3/10 - 12:59
 * @function
 */
public class Test {

    public static void main(String[] args) {
        MenuComponent AMenu = new Menu("早餐店菜单", "优质早餐供应");
        MenuComponent BMenu = new Menu("午餐店菜单", "放心午餐供应");
        MenuComponent B1Menu = new Menu("午餐甜点菜单", "午餐店甜点精选");
        MenuComponent CMenu = new Menu("咖啡厅菜单", "美味咖啡供应");

        MenuComponent allMenus = new Menu("所有菜单", "所有菜单");

        allMenus.add(AMenu);
        allMenus.add(BMenu);
        allMenus.add(CMenu);

        AMenu.add(new MenuItem("milk", "good milk", true, 10));
        BMenu.add(new MenuItem("burger", "good burger", false, 25));
        BMenu.add(B1Menu);
        B1Menu.add(new MenuItem("hotDog", "good hotDog", false, 17));
        CMenu.add(new MenuItem("coffee", "good coffee", true, 20));

        Waiter waiter = new Waiter(allMenus);
        waiter.printMenu();
    }

}

在这里插入图片描述

2.3 迭代器模式&组合模式小结

1,迭代器允许访问某个使用集合的类中集合的元素,且不会暴露该集合的内部结构

2,迭代器提供了一个通用的接口,迭代器将遍历集合的工作封装到了一个对象里

3,应该努力让一个类只有一个责任

4,组合模式提供一个结构,可以包含个别对象和组合对象

5,组合模式允许客户对个别对象和组合对象一视同仁,组合模式中的元素称为组件,组件可以是节点,也可以是叶子

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值