今天來818設計模式中的迭代器模式,也是java中Stack,List,Set等接口以及數組這個數據結構都會使用的一種模式。
首先,為什么使用迭代器模式,目的就是通過一個通用的迭代方法,隱藏stack,list,set以及數組中不同的遍歷細節。也就是說,我不想讓那些調用我的遍歷容器的方法的人知道我到底是怎么一個一個的獲取這些元素的(stack的pop,list的get,數組的array[i]),我只想讓他知道他能 通過一個迭代器Iterator或者通過一個for each語句就能拿到我容器里面所有的元素。這樣就能夠最大化的隱藏實現細節,封裝變化了。
先通過一個例子來一步步了解這其中的重要性吧。比方說,我要開發一個平臺,這個平臺會獲取到京東的訂單和淘寶的訂單,然后把訂單中的所有購買條目全部打印出來。
既然要打印訂單中的所有條目,那么就得先知道這些條目,也就是訂單項有哪些屬性。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
|
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(測試驅動開發)來做步驟實現。先寫好測試代碼,首先我要有個訂單接口,里面有兩個子類訂單(淘寶訂單和京東訂單):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
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循環來遍歷里面的內容。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
/* * %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接口
1
2
3
4
5
|
package iterator; public interface Order extends Iterable<Item>{ } |
注意上面的Order訂單接口繼承了Iterable接口之后,同樣也繼承過來了一個抽象方法iterator。這個抽象方法才是Iterable的根本實現方案。我們會在子類訂單中分別實現這個接口,然后提供京東和淘寶不同的迭代方案。
京東
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
|
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( "目前不支持刪除操作" ); } } } |
淘寶
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
|
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( "目前不支持刪除操作" ); } } } |
這樣,我就做到了提供一個標準的可以迭代的Order訂單接口,然后以兩種不同的迭代實現方案(京東、淘寶),為我們的測試類提供了一個可以屏蔽掉內部不同容器的具體實現區別。同時,這也是迭代器模式的運用。
總結:需求--不同容器不同迭代方案,改進--利用相同迭代方案來處理,將不同實現細節分別隱藏到容器自己的實現中。采用的方案就是實現Iterable接口,以及里面的Iterator方法,然后實現自己的迭代方式。
感謝閱讀,希望能幫助到大家,謝謝大家對本站的支持!