1,迭代器模式

一,前言

是的,我们第一个要介绍的设计模式,不是单例,也不是工厂,是迭代器
说一下先介绍迭代器的原因:
首先,这个模式很简单,透过这个设计模式,我们可以说一说设计模式的几个原则
其次,java和c#已经内置了迭代器,可以说这是一个已经废弃的设计模式,但依然有它的价值

二,背景

早点铺和炒菜馆要合并了,所以菜单也要合并在一起
早点铺和炒菜馆的菜单分别使用ArrayList和数组进行存储
使用不同的集合类型,导致遍历菜单时要采用对应的遍历方式

三,使用迭代器前的实现

1,新建MenuItem.java 菜单项

package com.brave.iterator_before;

/**
 * 菜单项对象
 * @author Brave
 *
 */
public class MenuItem {

    String name;    //名称
    String desc;    //描述
    boolean vegetarian;//是否素食
    double price;   //价格

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

    public String getName() {
        return name;
    }

    public String getDesc() {
        return desc;
    }

    public boolean isVegetarian() {
        return vegetarian;
    }

    public double getPrice() {
        return price;
    }

}

2,BreakfastMenu.java 早餐菜单-使用ArrayList存储数据

package com.brave.iterator_before;

import java.util.ArrayList;

/**
 * 使用ArrayList存储数据
 * @author Brave
 *
 */
public class BreakfastMenu {

    ArrayList menuItems;    //存储数据的集合

    /**
     * 初始化菜单集合并添加测试数据
     */
    public BreakfastMenu(){
        menuItems = new ArrayList();
        addItem("早餐A","早餐A描述",true,2.99);
    }

    /**
     * 添加菜单项方法
     */
    private void addItem(String name, String desc, boolean vegetarian, double price) {
        MenuItem menuItem = new MenuItem(name, desc, vegetarian, price);
        menuItems.add(menuItem);
    }

    /**
     * 获取菜单集合
     * @return
     */
    public ArrayList getMenuItems() {
        return menuItems;
    }

}

3,DinerMenu.java 晚餐菜单-使用数组存储数据

package com.brave.iterator_before;

/**
 * 使用数组形式存储数据
 * @author Brave
 *
 */
public class DinerMenu {

    static final int MAX_ITEMS = 6; // 数组初始化容量
    int numberOfItems = 0;          // 当前数组包含项目数量
    MenuItem[] menuItems;           // 存储菜单项的数组

    /**
     * 初始化菜单集合并添加数据
     */
    public DinerMenu(){
        menuItems = new MenuItem[MAX_ITEMS];
        addItem("晚餐A", "晚餐A描述", false, 2.99);
    }

    /**
     * 添加菜单项方法
     */
    private void addItem(String name, String desc, boolean vegetarian, double price) {
        MenuItem menuItem = new MenuItem(name, desc, vegetarian, price);
        if(numberOfItems >= MAX_ITEMS){
            System.err.println("菜单容量已满,不能再加入了");
        }else{
            menuItems[numberOfItems] = menuItem;
            numberOfItems++;
        }
    }

    /**
     * 获取菜单集合
     * @return
     */
    public MenuItem[] getMenuItems() {
        return menuItems;
    }

}

4,Client.java 客户端-遍历早餐和晚餐菜单

package com.brave.iterator_before;

import java.util.ArrayList;

public class Client {

    public static void main(String[] args) {

        //获取早餐数据源
        BreakfastMenu breakfastMenu = new BreakfastMenu();
        ArrayList breakfastItems = breakfastMenu.getMenuItems();

        //获取晚餐数据源
        DinerMenu dinerMenu = new DinerMenu();
        MenuItem[] dinerItems = dinerMenu.getMenuItems();

        // 早餐的遍历
        for(int i = 0; i < breakfastItems.size(); i++){
            MenuItem menuItem = (MenuItem)breakfastItems.get(i);
            System.out.println("name = " + menuItem.getName());
            System.out.println("price = " + menuItem.getPrice());
            System.out.println("desc = " + menuItem.getDesc());
        }

        // 晚餐的遍历
        for(int i = 0; i < dinerItems.length; i++){
            MenuItem menuItem = (MenuItem)dinerItems[i];
            if(menuItem != null){
                System.out.println("name = " + menuItem.getName());
                System.out.println("price = " + menuItem.getPrice());
                System.out.println("desc = " + menuItem.getDesc());
            }
        }
    }
}

5,运行结果

iterator_before_log

6,点评

以上代码做到了分别输出早餐和晚餐菜单,但是存在着很多的问题

首先,客户端需要知道早餐和晚餐菜单的具体实现,依赖于具体而没有依赖抽象

其次,早餐和晚餐菜单采用不同的数据源存储数据,造成输出遍历的方式也不同
    抛开返回的数据存储类型不同其他逻辑相似,这里我们能否封装一下使之返回相同的接口类型

带着这两个问题,我们看一下使用迭代器的代码

四,罗列问题

上边的场景最大的问题在于,早餐和晚餐返回的容器是不一样的,这导致我们遍历时需要分别按照各自的方式去遍历
如果我们可以将遍历的过程做一个封装,使之返回相同的对象,那对于外部的调用将带来很大的便利

所以我们看一下有问题的代码段

1,不是面向接口编程,依赖了具体实现而非抽象

        BreakfastMenu breakfastMenu = new BreakfastMenu();
        DinerMenu dinerMenu = new DinerMenu();

2,返回的集合类型不同,导致需要分别采用不同的方式进行遍历,并且暴露了遍历的细节

        BreakfastMenu breakfastMenu = new BreakfastMenu();
        ArrayList breakfastItems = breakfastMenu.getMenuItems();

        DinerMenu dinerMenu = new DinerMenu();
        MenuItem[] dinerItems = dinerMenu.getMenuItems();

        for(int i = 0; i < breakfastItems.size(); i++){
            MenuItem menuItem = (MenuItem)breakfastItems.get(i);
            System.out.println("name = " + menuItem.getName());
            System.out.println("price = " + menuItem.getPrice());
            System.out.println("desc = " + menuItem.getDesc());
        }

        for(int i = 0; i < dinerItems.length; i++){
            MenuItem menuItem = (MenuItem)dinerItems[i];
            if(menuItem != null){
                System.out.println("name = " + menuItem.getName());
                System.out.println("price = " + menuItem.getPrice());
                System.out.println("desc = " + menuItem.getDesc());
            }
        }

五,解决问题-使用迭代器

1,封装遍历的迭代器接口

package com.brave.iterator_after;

/**
 * 迭代器接口
 * @author Brave
 *
 */
public interface Iterator {
    boolean hasNext();  // 是否还有下一个
    Object next();      // 返回下一个对象
}

2,创建早餐和晚餐类对应的迭代器实现-将集合的遍历方式进行分装

breakfastMenuIterator.java

package com.brave.iterator_after;

import java.util.ArrayList;

/**
 * 晚餐迭代器具体实现
 * 实现了迭代器接口,封装了遍历过程,返回Iterator对象
 * @author Brave
 *
 */
public class breakfastMenuIterator implements Iterator {

    ArrayList<MenuItem> menuItems;
    int position = 0;

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

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

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

}

DinerMenuIterator.java

package com.brave.iterator_after;

/**
 * 晚餐迭代器具体实现
 * 实现了迭代器接口,封装了遍历过程,返回Iterator对象
 * @author Brave
 *
 */
public class DinerMenuIterator implements Iterator {

    MenuItem[] menuItems;
    int position = 0;

    public DinerMenuIterator(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++;
        return menuItem;
    }

}

这样我们就封装了早餐和晚餐的遍历过程,只需要获取迭代器,即可对其进行遍历


2,修改早餐和午餐菜单类,使他们分别返回各自的迭代器

创建菜单接口,实现返回迭代器–面向接口编程

package com.brave.iterator_after;

public interface Menu {
    Iterator createIterator();
}

早餐和完成菜单类分别实现Menu接口并返回各自的迭代器对象

修改后的 BreakfastMenu.java

package com.brave.iterator_after;

import java.util.ArrayList;

/**
 * 使用ArrayList存储数据
 * @author Brave
 *
 */
public class BreakfastMenu implements Menu{

    ArrayList menuItems;    //存储数据的集合

    /**
     * 初始化菜单集合并添加测试数据
     */
    public BreakfastMenu(){
        menuItems = new ArrayList();
        addItem("煎饼果子", "鸡蛋,果子", false, 5);
        addItem("大饼夹一切", "大饼,鸡排等", false, 10);
        addItem("豆浆", "豆浆", true, 2);
        addItem("豆腐脑", "豆腐脑", true, 3);
        addItem("锅巴菜", "锅巴菜", true, 4);
        addItem("茶叶蛋", "鸡蛋", false, 1);
        addItem("云吞", "猪肉,面皮,高汤", false, 4);
    }

    /**
     * 添加菜单项方法
     */
    private void addItem(String name, String desc, boolean vegetarian, double price) {
        MenuItem menuItem = new MenuItem(name, desc, vegetarian, price);
        menuItems.add(menuItem);
    }

//  /**
//   * 获取菜单集合
//   * @return
//   */
//  public ArrayList getMenuItems() {
//      return menuItems;
//  }

    @Override
    public Iterator createIterator(){
        return new breakfastMenuIterator(menuItems);
    }

}

修改后的 DinerMenu.java

package com.brave.iterator_after;

/**
 * 使用数组形式存储数据
 * @author Brave
 *
 */
public class DinerMenu implements Menu{

    static final int MAX_ITEMS = 6; // 数组初始化容量
    int numberOfItems = 0;          // 当前数组包含项目数量
    MenuItem[] menuItems;           // 存储菜单项的数组

    /**
     * 初始化菜单集合并添加数据
     */
    public DinerMenu(){
        menuItems = new MenuItem[MAX_ITEMS];
        addItem("京酱肉丝", "肉丝,葱,豆皮", false, 25);
        addItem("五谷丰登", "各种菜", true, 10);
        addItem("水果沙拉", "各种水果", true, 15);
        addItem("宫保鸡丁", "鸡丁,葱,姜,蒜,微辣", true, 15);
    }

    /**
     * 添加菜单项方法
     */
    private void addItem(String name, String desc, boolean vegetarian, double price) {
        MenuItem menuItem = new MenuItem(name, desc, vegetarian, price);
        if(numberOfItems >= MAX_ITEMS){
            System.err.println("菜单容量已满,不能再加入了");
        }else{
            menuItems[numberOfItems] = menuItem;
            numberOfItems++;
        }
    }

//  /**
//   * 获取菜单集合
//   * @return
//   */
//  public MenuItem[] getMenuItems() {
//      return menuItems;
//  }

    @Override
    public Iterator createIterator(){
        return new DinerMenuIterator(menuItems);
    }

}

创建一个招待员类,对菜单进行遍历

package com.brave.iterator_after;

/**
 * 招待员类
 * 面向接口编程 Menu, Iterator
 * 封装了Menu遍历
 * @author Brave
 *
 */
public class Waitress {

    Menu breakfastMenu;
    Menu dinerMenu;

    public Waitress(Menu breakfastMenu, Menu dinerMenu) {
        this.breakfastMenu = breakfastMenu;
        this.dinerMenu = dinerMenu;
    }

    public void printMenu(){

        Iterator breakfastIterator = breakfastMenu.createIterator();
        Iterator dinerIterator = dinerMenu.createIterator();

        System.out.println("----开始输出早餐Menu----");
        printMenu(breakfastIterator);
        System.out.println("----开始输出晚餐Menu----");
        printMenu(dinerIterator);
    }

    private void printMenu(Iterator iterator) {
        while(iterator.hasNext()){
            MenuItem menuItem = (MenuItem)iterator.next();
            System.out.println("name = " + menuItem.getName() + ", desc = " + menuItem.getDesc() + ", price = " + menuItem.getPrice());
        }
    }

}

我们可以看到招待员这个类的特点

1,面向接口编程:不在需要依赖具体的早餐晚餐实现类,只需要依赖Menu接口
2,对于遍历部分也是面向Iterator接口编程,不同的容器遍历,现在只需要返回相同的迭代器,实现客户端调用的一致性
3,对集合的遍历进行了封装,不需要知道集合内部是如何进行遍历的

创建Client.java调用招待员类,并查看输出

package com.brave.iterator_after;

import java.util.ArrayList;

public class client {

    public static void main(String[] args) {

        /**
         * 面向接口编程
         */
        Menu breakfastMenu = new BreakfastMenu();
        Menu dinerMenu = new DinerMenu();

        /**
         * 封装遍历逻辑:不需要知道菜单项是如何实现的
         */
        Waitress waitress = new Waitress(breakfastMenu, dinerMenu);
        waitress.printMenu();

    }
}

运行结果:

Iterator_after_log


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

BraveWangDev

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值