设计模式学习之路 - 迭代器模式 - 封装遍历

今天了解一下迭代器模式。

说到迭代器, 有点编程经验的应该都知道 Iterator..不错,这个就是迭代器。

有时候在走循环流程,我们通常会拿到容器中的迭代器,通过迭代器进行循环。

什么叫迭代器模式呢:提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示

这边举个小例子解释一下。

有两家餐厅要合并了,一个是中式餐馆的, 一个是港式茶点的,因为合并了,两种东西都卖,点餐的人希望同时可以点两种不同的东西。

但是有个问题,在他们的菜单中,尽管菜单的明细情况都一样,其中一个是用数组实现的,一个是却用链表实现的(这里暂且不讨论数组和链表的优劣势)。

这个是菜单的明细,包含了菜品的名字,描述和价格。

package com.chris.iterator;

public class MenuItem {
	String name;
	String description;
	double price;

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

	public String getName() {
		return name;
	}

	public String getDescription() {
		return description;
	}

	public double getPrice() {
		return price;
	}
}

中式餐厅的菜单是用数组去保存菜单项的。

package com.chris.iterator;

public class ChineseMenu {
	static final int MAX_ITEMS = 10;
	int numberOfItems = 0;
	MenuItem[] menuItems;
	
	public ChineseMenu(){
		menuItems = new MenuItem[MAX_ITEMS];
		addItem("剁椒鱼头", "湘菜,非常美味的剁椒鱼头", 50);
		addItem("回锅肉", "特别好吃", 30);
		addItem("干锅花菜", "用干锅弄的花菜,十分美味", 24);
		addItem("东坡肉", "苏东坡的肉?", 42);
	}
	
	public void addItem(String name, String description, double price){
		MenuItem menuItem = new MenuItem(name, description, price);
		if(numberOfItems >= MAX_ITEMS){
			System.err.println("sorry, menu is full! Can't add item to menu");
		}else{
			menuItems[numberOfItems] = menuItem;
			numberOfItems++; 
		}
	}
	
	public MenuItem[] getMenuItems(){
		return menuItems;
	}
}

而港式茶餐厅的菜单时用链表去保存菜单的。

package com.chris.iterator;

import java.util.ArrayList;

public class HongKongMenu {
	ArrayList menuItems;
	
	public HongKongMenu(){
		menuItems = new ArrayList();
		addItem("水晶虾饺", "里面有虾,很好吃", 18);
		addItem("豉汁蒸排骨", "要等一段时间,但是很美味", 20);
		addItem("流沙包", "甜甜的,超好吃", 10);
		addItem("叉烧包", "广东特色,不错", 12);
	}
	
	public void addItem(String name, String description, double price){
		MenuItem menuItem = new MenuItem(name, description, price);
		menuItems.add(menuItem);
	}
	
	public ArrayList getMenuItems(){
		return menuItems;
	}
}

看起来好像没有什么问题,但是每次服务员在展示菜单时,需要先创建两个菜单,再获取里面的菜单,

由于菜单的容器不同,需要 再分别通过两个循环进行展示,打印出菜单的情况(如果再合并一个餐厅,就要三个循环。。)


基于两边菜单都不愿意改变自身的实现,可能由于其他关联太多(实际开发中这种情况也是会有的)。

于是,我们开始考虑其他的解决方案,封装遍历!!

我们只需要封装变化的部分,而这里面的变化,就是两种容器遍历方法和获取元素的方法不同。

链表是通过.size() 和get(index), 数组是通过.length 和 [index],这导致他们的循环不能统一。


我们开始引入迭代器模式,创建迭代器的接口,简单的不能再简单了,只有两个方法,获取下一个元素,判断是否有下一个元素。

package com.chris.iterator;

public interface Iterator {
	boolean hasNext();
	Object next();
}

我们为中式餐厅做一个迭代器,为他的菜单服务,实现了上面接口的两个方法。

package com.chris.iterator;

public class ChineseMenuIterator implements Iterator{
	
	MenuItem[] items;
	int position = 0;

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

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

}

同样的, 也为港式茶餐厅做一个迭代器,为他们的菜单服务。

package com.chris.iterator;

import java.util.ArrayList;

public class HongKongMenuIterator implements Iterator {
	ArrayList menuItems;
	int position = 0;

	public HongKongMenuIterator(ArrayList menuItems) {
		this.menuItems = menuItems;
	}

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

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

}

然后在各自的菜单中,把获取菜单的方法删除, 分别增加一个方法,返回各自的迭代器,这样就OK了。

然后,服务员报菜单就方便多啦,不需要几次循环了,只需要通过统一的遍历将元素循环打印出来,代码瞬间变得优雅起来。

package com.chris.iterator;

public class Waitress {
	ChineseMenu chineseMenu;
	HongKongMenu hongKongMenu;
	
	public Waitress(ChineseMenu chineseMenu, HongKongMenu hongKongMenu){
		this.chineseMenu = chineseMenu;
		this.hongKongMenu = hongKongMenu;
	}
	
	public void printMenu(){
		Iterator chineseMenuIterator = chineseMenu.createIterator();
		Iterator hongKongMenuIterator = hongKongMenu.createIterator();
		System.out.println("Chinese food:");
		printMenu(chineseMenuIterator);
		System.out.println("HongKong food:");
		printMenu(hongKongMenuIterator);
	}
	
	private void printMenu(Iterator iterator){
		while(iterator.hasNext()){
			MenuItem menuItem = (MenuItem) iterator.next();
			System.out.print(menuItem.getName() + "-");
			System.out.print(menuItem.getDescription() + "-");
			System.out.println(menuItem.getPrice());
		}
	}
}

测试代码这里就不贴了,应该结果一目了然。


我们只是增加一个接口, 然后通过各自实现接口,将自己的逻辑封装起来,就可以把流程统一起来。

这里就用到面向对象的一个很重要的思想:要针对接口编程,而不要针对实现编程

这也是大家今后在编程的时候要多考虑的地方,这样才能有足够的扩展性,而不是把代码写死。


其实,在java的基本库中就有Iterator接口,而且他相较我们自己写的接口,还多了一个remove方法。

这就是最开始提到的默认的迭代器,有很多容器是在其实现类内部维护一个自身的迭代器, 我们可以通过直接获取这个迭代器进行使用。

而没有的,如果我们需要有类似上述需求的话,也可以自己实现。


迭代器模式就到这了,如果文中有什么不妥甚至错误的地方,还望纠正,和大家共勉!!


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值