今天来818设计模式中的迭代器模式,也是java中Stack,List,Set等接口以及数组这个数据结构都会使用的一种模式。
首先,为什么使用迭代器模式,目的就是通过一个通用的迭代方法,隐藏stack,list,set以及数组中不同的遍历细节。也就是说,我不想让那些调用我的遍历容器的方法的人知道我到底是怎么一个一个的获取这些元素的(stack的pop,list的get,数组的array[i]),我只想让他知道他能 通过一个迭代器Iterator或者通过一个for each语句就能拿到我容器里面所有的元素。这样就能够最大化的隐藏实现细节,封装变化了。
先通过一个例子来一步步了解这其中的重要性吧。比方说,我要开发一个平台,这个平台会获取到京东的订单和淘宝的订单,然后把订单中的所有购买条目全部打印出来。
既然要打印订单中的所有条目,那么就得先知道这些条目,也就是订单项有哪些属性。
package iterator;
/**
*
* @ClassName: Item
* @Description: 订单项
* @author minjun
*
*/
public class Item {
/**商品名称*/
private String name;
/**价格*/
private double price;
/**描述*/
private String desc;
/**数量*/
private int count;
public Item(String name, double price, String desc, int count) {
this.name = name;
this.price = price;
this.desc = desc;
this.count = count;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
@Override
public String toString() {
return "Item [name=" + name + ", price=" + price + ", desc=" + desc
+ ", count=" + count + "]";
}
}
知道了这个条目,然后我想看看京东和淘宝是如何存储这些条目的。于是我问了问刘强东和马云,得知京东是用集合List存储,因为方便,而淘宝是用数组存储,因为看起来更装逼。他们都不愿意修改存储的容器,因为改动太大。
这时, 如果用传统想法,ok,我拿到京东的List,然后通过for循环和list.get(i)获取里面的每个条目并打印。然后拿到淘宝的array,通过for循环和array[i]获取里面的条目并打印。是不是可以实现呢?确实可以,但是我发现这样的话,每个容器我都要实现一遍不同的打印方法。目前是两个倒还好,如果又来个谁谁谁,用链表来实现容器,那我是不是又要新加一个迭代链表的方法呢?我当然不会愿意,因为这样太麻烦了。于是乎,我有个想法,思路是这样的:
我希望让京东的订单和淘宝的订单都是可以方便的遍历里面的元素,遍历的方法能够通过一个公共的方法来处理,而不是像之前那个分别做处理。根据这个思路,用TDD(测试驱动开发)来做步骤实现。先写好测试代码,首先我要有个订单接口,里面有两个子类订单(淘宝订单和京东订单):
package iterator;
import org.junit.Test;
public class TestCase {
@Test
public void test() {
Order o =
new TBOrder();//淘宝的订单
// new JDOrder();//京东的订单
printOrder(o);//打印订单
}
/**打印订单 */
private void printOrder(Order o) {
for (Item item : o) {
System.out.println(item);
}
}
}
如果能像上述这样打印,那会多么方便啊。如果换成淘宝订单,就用淘宝的订单迭代实现,换成京东的订单,就用京东的订单实现,我在测试代码根本不需要关注实现细节。现在我会想,如果能通过什么方法直接打印这个订单Order中的所有条目,那才能完整的实现我上述的代码。也就是说我需要我的订单是可以遍历的,那应该怎么做呢?其实java中提供了这样的接口,就是Iterable,如果我的订单都实现了这个接口,那么我的订单自然而然就可以通过一个for each循环来遍历里面的内容。
/*
* %W% %E%
*
* Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved.
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*/
package java.lang;
import java.util.Iterator;
/** Implementing this interface allows an object to be the target of
* the "foreach" statement.
* @since 1.5
*/
public interface Iterable<T> {
/**
* Returns an iterator over a set of elements of type T.
*
* @return an Iterator.
*/
Iterator<T> iterator();
}
上面是java的Iterable接口,下面是我自己的订单接口,继承了Iterable接口
package iterator;
public interface Order extends Iterable<Item>{
}
注意上面的Order订单接口继承了Iterable接口之后,同样也继承过来了一个抽象方法iterator。这个抽象方法才是Iterable的根本实现方案。我们会在子类订单中分别实现这个接口,然后提供京东和淘宝不同的迭代方案。
京东
package iterator;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
*
* @ClassName: JDOrder
* @Description: 京东订单
* @author minjun
*
*/
public class JDOrder implements Order {
/** 京东用集合装订单项 */
private List<Item> list = new ArrayList<Item>();
public JDOrder() {
add("iphone6", 5000.00, "一部手机", 2);
add("mbp", 16000.00, "一台电脑", 1);
add("西门子洗衣机", 3000.00, "一台洗衣机", 3);
}
/** 添加订单条目 */
public void add(String name, double price, String desc, int count) {
list.add(new Item(name, price, desc, count));
}
@Override
public Iterator<Item> iterator() {
return new MyIterator();
}
private class MyIterator implements Iterator<Item> {
private Iterator<Item> it = list.iterator();
@Override
public boolean hasNext() {
return it.hasNext();
}
@Override
public Item next() {
return it.next();
}
@Override
public void remove() {
throw new UnsupportedOperationException("目前不支持删除操作");
}
}
}
淘宝
package iterator;
import java.util.Iterator;
import java.util.NoSuchElementException;
/**
*
* @ClassName: TBOrder
* @Description: 淘宝订单
* @author minjun
*
*/
public class TBOrder implements Order{
private int size=3;
private Item[] orders=new Item[size];
private int index=0;
public TBOrder(){
add("天猫1", 1111, "天猫活动1", 1);
add("天猫2", 1111, "天猫活动1", 1);
add("天猫3", 1111, "天猫活动1", 1);
add("天猫4", 1111, "天猫活动1", 1);
add("天猫5", 1111, "天猫活动1", 1);
add("天猫6", 1111, "天猫活动1", 1);
add("天猫7", 1111, "天猫活动1", 1);
add("天猫8", 1111, "天猫活动1", 1);
}
/**添加订单条目*/
public void add(String name, double price, String desc, int count) {
//如果超过数组大小,就扩容
if(index>=size-1){
resize();
}
orders[index++]=new Item(name, price, desc, count);
}
/**扩容*/
private void resize() {
size=size<<1;//移位运算符--相当于size=size*2
Item[] newItems=new Item[size];
//将原始数组内容拷贝到新数组中去
for(int i=0;i<orders.length;i++){
newItems[i]=orders[i];
}
orders=newItems;
}
@Override
public Iterator<Item> iterator() {
return new MyIterator();
}
private class MyIterator implements Iterator<Item>{
private int curr=0;
@Override
public boolean hasNext() {
return orders[curr]!=null;
}
@Override
public Item next() {
if(hasNext()){
return orders[curr++];
}else{
throw new NoSuchElementException("没有这个元素");
}
}
@Override
public void remove() {
throw new UnsupportedOperationException("目前不支持删除操作");
}
}
}
总结:需求--不同容器不同迭代方案,改进--利用相同迭代方案来处理,将不同实现细节分别隐藏到容器自己的实现中。采用的方案就是实现Iterable接口,以及里面的Iterator方法,然后实现自己的迭代方式。