一,前言
是的,我们第一个要介绍的设计模式,不是单例,也不是工厂,是迭代器
说一下先介绍迭代器的原因:
首先,这个模式很简单,透过这个设计模式,我们可以说一说设计模式的几个原则
其次,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,运行结果
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();
}
}
运行结果: