设计模式学习笔记(九)——迭代器模式和组合模式

本文探讨了迭代器模式和组合模式在解决不同菜单结构问题中的应用,通过具体示例展示了如何利用这两种设计模式实现菜单的灵活遍历和多层次结构处理。

迭代器模式

定义: 提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示
分析: 有许多种方法可以将对象堆起来成为一个集合,例如数组,堆栈或是列表,当客户想要对其进行遍历时,我们不应该让其看到内部实现。使用一个迭代器来进行遍历

UML类图
在这里插入图片描述
Aggregate: 共同的接口供所有的聚合使用
ConcreteAggregate: 具体聚合,持有一个对象的集合,实现一个返回集合的迭代器的方法,负责实例化一个具体迭代器,用于遍历对象集合
Iterator: 迭代器的父接口,包含一些用于遍历的方法
ConcreteIterator: 具体迭代器,负责管理目前遍历的位置

示例
有两家餐馆进行合并,计划使用餐馆A菜单作为早餐,餐馆B菜单作为午餐。问题在于A菜单使用ArrayList用来存储菜单项,B菜单使用数组,而两边都不愿进行改变。对于客户代码,总是需要使用处理两个菜单,并使用两个循环遍历,如果有第三个菜单,甚至是第四个呢?

菜单项

public class MenuItem {

    String name;
    String description;
    boolean vegetarian;
    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;
    }
}

具体菜单

//早餐菜单
public class BreakfastMenu {

    static final int MAX_ITEMS = 6;
    int numberOfItems = 0;
    ArrayList<MenuItem> arrayList;

    public BreakfastMenu(ArrayList<MenuItem> arrayList) {
        this.arrayList = arrayList;
    }

    public BreakfastMenu() {

    }

    public void addItem(String name, String description, boolean vegetarian, double price){
        MenuItem menuItem = new MenuItem(name, description, vegetarian, price);
        if(numberOfItems >= MAX_ITEMS){
            System.out.println("菜单没有位置");
        } else {
            arrayList.add(menuItem);
            numberOfItems += 1;
        }
    }

    public Iterator createIterator(){
        return new IteratorA(arrayList);
    }

    public ArrayList<MenuItem> getMenuItems(){
        return arrayList;
    }
}
//晚餐菜单
public class DinnerMenu {

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

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

    public DinnerMenu() {
    }

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

    public Iterator createIterator(){
        return new IteratorB(menuItems);
    }

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

迭代器接口

public interface Iterator {

    boolean hasNext();
    Object next();
}

具体迭代器

public class IteratorA implements Iterator {

    ArrayList<MenuItem> arrayList;
    int position = 0;

    public IteratorA(ArrayList arrayList) {
        this.arrayList = arrayList;
    }

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

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

public class IteratorB implements Iterator {

    MenuItem[] menuItems;
    int position = 0;

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

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

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

客户代码

public class Waitress {

    BreakfastMenu breakfastMenu;
    DinnerMenu dinnerMenu;

    public Waitress(BreakfastMenu breakfastMenu, DinnerMenu dinnerMenu) {
        this.breakfastMenu = breakfastMenu;
        this.dinnerMenu = dinnerMenu;
    }

    public Waitress() {
    }

    public void printMenu(){
        Iterator iteratorA = breakfastMenu.createIterator();
        Iterator iteratorB = dinnerMenu.createIterator();
        System.out.println("\n-------早餐菜单-------");
        printMenu(iteratorA);
        System.out.println("\n-------晚餐菜单--------");
        printMenu(iteratorB);
    }

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

测试

public class test {
    public static void main(String[] args) {
        BreakfastMenu breakfastMenu = new BreakfastMenu(new ArrayList<MenuItem>());
        DinnerMenu dinnerMenu = new DinnerMenu(new MenuItem[6]);
        breakfastMenu.addItem("煎饼","煎",true,6);
        breakfastMenu.addItem("包子","蒸",false,1);
        breakfastMenu.addItem("油条","炸",true,2);

        dinnerMenu.addItem("手撕包菜","酸辣",true,10);
        dinnerMenu.addItem("鱼香肉丝","酸甜",false,12);
        dinnerMenu.addItem("辣子鸡","辣",false,14);
        Waitress waitress = new Waitress(breakfastMenu, dinnerMenu);

        waitress.printMenu();
    }
}

在这里插入图片描述
做了什么事情?
我们不改变两个餐馆菜单的内部实现,各自提供一个迭代器,只需要实现一个create方法,客户不知道菜单内部实现,使得客户代码更容易维护和扩展。

优化
现在的客户代码还是依赖于菜单的具体实现,我们将两个菜单类实现一个共同接口,则客户代码可以使用接口来调用需要的菜单对象,减少客户和具体类的依赖

public interface Menu {
    public Iterator createIterator();
}

同时,JAVA提供了java.util.Iterator接口,我们可以直接实现该接口,并且ArrayList也内置了Iterator。

设计原则: 单一原则,一个类应该只有一个引起变化的原因。
我们知道要避免类内的改变,因为修改代码很容易造成很多潜在的错误。如果一个类内具有两个改变的原因,这会使得将来该类的变化几率上升,例如当我们允许一个类完成自己的事情(管理某种聚合),同时还有担负更多责任(例如遍历),我们就给了这个类两个变化的原因。

新的问题: 现在,他们希望在午餐菜单加上一份饭后甜点的“子菜单”,我们不仅仅要支持多个菜单,甚至还有支持菜单中的菜单
现在,菜单内部实现的修改已经不可避免了。我们有新的需求

  • 我们需要某种树形结构,以容纳菜单,子菜单,和菜单项
  • 我们要确定能够在每个菜单的每个项之间游走,至少要向现在的迭代器一样方便
  • 我们也需要更有弹性的进行遍历,比如只需要遍历甜点

组合模式

定义: 允许你将对象组合成树形结构来表现“整体/部分”层次结构。组合能让客户以一致的方式处理个别对象以及对象组合

分析: 以我们的菜单为例子来思考这一切,这个模式创建一个树形结构,在同一结构中处理嵌套菜单和菜单项组,我们得到一个“整体/部分"层次结构,即一个对象树,也可以看做一个大的菜单。所以我们可以”统一处理个别对象和组合对象“,这意味着,如果我们有一个树形结构的菜单,子菜单和可能带有菜单项的子菜单,那么任何一个菜单都是一种”组合“,因为它既可以包含其他菜单,也可以包含菜单项。个别对象只能是菜单项,并未持有其他对象。

UML类图
在这里插入图片描述

Component: 所以对象的父接口,不管是组合还是叶节点
Leaf: 没有子节点,实现了operation方法
Composite: 实现了叶节点的相关操作,它的角色是要定义组件的行为,而这样的组件具有字节点

组件接口

public abstract class MenuComponent {

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

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

    public MenuComponent getChild(){
        throw new UnsupportedOperationException();
    }

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

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

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

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

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

组合

public class Menu extends MenuComponent {
    ArrayList<MenuComponent> menuComponents = new ArrayList<>();
    String name;
    String description;

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

    public Menu() {
    }

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

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

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

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

    public String getDescription() {
        return description;
    }

    public void print(){
        System.out.print("\n"+getName());
        System.out.println(","+getDescription());
        System.out.println("--------------------");

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

public class MenuItem extends MenuComponent {
    private String name;
    private String description;
    private boolean isVegetarian;
    private double price;

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

    public MenuItem() {
    }

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

    public String getDescription() {
        return description;
    }

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

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

    public void print(){
        System.out.print("    "+getName());
        if(isVegetarian()){
            System.out.print(","+"素食");
        }
        System.out.print(","+getDescription());
        System.out.println("-----"+getPrice());
    }
}

客户代码

public class Waitress {
    MenuComponent allMenus;

    public Waitress(MenuComponent allMenus) {
        this.allMenus = allMenus;
    }

    public Waitress() {
    }

    public void printMenu(){
        allMenus.print();
    }
}

测试

public class test {
    public static void main(String[] args) {
        MenuComponent menuA = new Menu("pancake", "早餐");
        MenuComponent menuB = new Menu("dinner menu", "午餐");
        MenuComponent menuC = new Menu("cafe menu", "甜点");

        MenuComponent allMenus = new Menu("allmenus","顶层菜单");

        allMenus.add(menuA);
        allMenus.add(menuB);
        allMenus.add(menuC);

        menuA.add(new MenuItem("豆浆","饮品",true,2));
        menuB.add(new MenuItem("牛排","正餐",false,20));
        menuB.add(menuC);
        menuC.add(new MenuItem("黑咖啡","甜点",true,10));

        Waitress waitress = new Waitress(allMenus);
        waitress.printMenu();
    }
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值