java 迭代器模式實(shí)例詳解
java 迭代器模式實(shí)例詳解
今天來(lái)818設(shè)計(jì)模式中的迭代器模式,也是java中Stack,List,Set等接口以及數(shù)組這個(gè)數(shù)據(jù)結(jié)構(gòu)都會(huì)使用的一種模式。
首先,為什么使用迭代器模式,目的就是通過(guò)一個(gè)通用的迭代方法,隱藏stack,list,set以及數(shù)組中不同的遍歷細(xì)節(jié)。也就是說(shuō),我不想讓那些調(diào)用我的遍歷容器的方法的人知道我到底是怎么一個(gè)一個(gè)的獲取這些元素的(stack的pop,list的get,數(shù)組的array[i]),我只想讓他知道他能 通過(guò)一個(gè)迭代器Iterator或者通過(guò)一個(gè)for each語(yǔ)句就能拿到我容器里面所有的元素。這樣就能夠最大化的隱藏實(shí)現(xiàn)細(xì)節(jié),封裝變化了。
先通過(guò)一個(gè)例子來(lái)一步步了解這其中的重要性吧。比方說(shuō),我要開(kāi)發(fā)一個(gè)平臺(tái),這個(gè)平臺(tái)會(huì)獲取到京東的訂單和淘寶的訂單,然后把訂單中的所有購(gòu)買(mǎi)條目全部打印出來(lái)。
既然要打印訂單中的所有條目,那么就得先知道這些條目,也就是訂單項(xiàng)有哪些屬性。
package iterator;
/**
*
* @ClassName: Item
* @Description: 訂單項(xiàng)
* @author minjun
*
*/
public class Item {
/**商品名稱(chēng)*/
private String name;
/**價(jià)格*/
private double price;
/**描述*/
private String desc;
/**數(shù)量*/
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 + "]";
}
}
知道了這個(gè)條目,然后我想看看京東和淘寶是如何存儲(chǔ)這些條目的。于是我問(wèn)了問(wèn)劉強(qiáng)東和馬云,得知京東是用集合List存儲(chǔ),因?yàn)榉奖?,而淘寶是用?shù)組存儲(chǔ)。他們都不愿意修改存儲(chǔ)的容器,因?yàn)楦膭?dòng)太大。
這時(shí), 如果用傳統(tǒng)想法,ok,我拿到京東的List,然后通過(guò)for循環(huán)和list.get(i)獲取里面的每個(gè)條目并打印。然后拿到淘寶的array,通過(guò)for循環(huán)和array[i]獲取里面的條目并打印。是不是可以實(shí)現(xiàn)呢?確實(shí)可以,但是我發(fā)現(xiàn)這樣的話(huà),每個(gè)容器我都要實(shí)現(xiàn)一遍不同的打印方法。目前是兩個(gè)倒還好,如果又來(lái)個(gè)誰(shuí)誰(shuí)誰(shuí),用鏈表來(lái)實(shí)現(xiàn)容器,那我是不是又要新加一個(gè)迭代鏈表的方法呢?我當(dāng)然不會(huì)愿意,因?yàn)檫@樣太麻煩了。于是乎,我有個(gè)想法,思路是這樣的:
我希望讓京東的訂單和淘寶的訂單都是可以方便的遍歷里面的元素,遍歷的方法能夠通過(guò)一個(gè)公共的方法來(lái)處理,而不是像之前那個(gè)分別做處理。根據(jù)這個(gè)思路,用TDD(測(cè)試驅(qū)動(dòng)開(kāi)發(fā))來(lái)做步驟實(shí)現(xiàn)。先寫(xiě)好測(cè)試代碼,首先我要有個(gè)訂單接口,里面有兩個(gè)子類(lèi)訂單(淘寶訂單和京東訂單):
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);
}
}
}
如果能像上述這樣打印,那會(huì)多么方便啊。如果換成淘寶訂單,就用淘寶的訂單迭代實(shí)現(xiàn),換成京東的訂單,就用京東的訂單實(shí)現(xiàn),我在測(cè)試代碼根本不需要關(guān)注實(shí)現(xiàn)細(xì)節(jié)?,F(xiàn)在我會(huì)想,如果能通過(guò)什么方法直接打印這個(gè)訂單Order中的所有條目,那才能完整的實(shí)現(xiàn)我上述的代碼。也就是說(shuō)我需要我的訂單是可以遍歷的,那應(yīng)該怎么做呢?其實(shí)java中提供了這樣的接口,就是Iterable,如果我的訂單都實(shí)現(xiàn)了這個(gè)接口,那么我的訂單自然而然就可以通過(guò)一個(gè)for each循環(huán)來(lái)遍歷里面的內(nèi)容。
/*
* %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接口之后,同樣也繼承過(guò)來(lái)了一個(gè)抽象方法iterator。這個(gè)抽象方法才是Iterable的根本實(shí)現(xiàn)方案。我們會(huì)在子類(lèi)訂單中分別實(shí)現(xiàn)這個(gè)接口,然后提供京東和淘寶不同的迭代方案。
京東
package iterator;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
*
* @ClassName: JDOrder
* @Description: 京東訂單
* @author minjun
*
*/
public class JDOrder implements Order {
/** 京東用集合裝訂單項(xiàng) */
private List<Item> list = new ArrayList<Item>();
public JDOrder() {
add("iphone6", 5000.00, "一部手機(jī)", 2);
add("mbp", 16000.00, "一臺(tái)電腦", 1);
add("西門(mén)子洗衣機(jī)", 3000.00, "一臺(tái)洗衣機(jī)", 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, "天貓活動(dòng)1", 1);
add("天貓2", 1111, "天貓活動(dòng)1", 1);
add("天貓3", 1111, "天貓活動(dòng)1", 1);
add("天貓4", 1111, "天貓活動(dòng)1", 1);
add("天貓5", 1111, "天貓活動(dòng)1", 1);
add("天貓6", 1111, "天貓活動(dòng)1", 1);
add("天貓7", 1111, "天貓活動(dòng)1", 1);
add("天貓8", 1111, "天貓活動(dòng)1", 1);
}
/**添加訂單條目*/
public void add(String name, double price, String desc, int count) {
//如果超過(guò)數(shù)組大小,就擴(kuò)容
if(index>=size-1){
resize();
}
orders[index++]=new Item(name, price, desc, count);
}
/**擴(kuò)容*/
private void resize() {
size=size<<1;//移位運(yùn)算符--相當(dāng)于size=size*2
Item[] newItems=new Item[size];
//將原始數(shù)組內(nèi)容拷貝到新數(shù)組中去
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("沒(méi)有這個(gè)元素");
}
}
@Override
public void remove() {
throw new UnsupportedOperationException("目前不支持刪除操作");
}
}
}
這樣,我就做到了提供一個(gè)標(biāo)準(zhǔn)的可以迭代的Order訂單接口,然后以?xún)煞N不同的迭代實(shí)現(xiàn)方案(京東、淘寶),為我們的測(cè)試類(lèi)提供了一個(gè)可以屏蔽掉內(nèi)部不同容器的具體實(shí)現(xiàn)區(qū)別。同時(shí),這也是迭代器模式的運(yùn)用。
總結(jié):需求--不同容器不同迭代方案,改進(jìn)--利用相同迭代方案來(lái)處理,將不同實(shí)現(xiàn)細(xì)節(jié)分別隱藏到容器自己的實(shí)現(xiàn)中。采用的方案就是實(shí)現(xiàn)Iterable接口,以及里面的Iterator方法,然后實(shí)現(xiàn)自己的迭代方式。
感謝閱讀,希望能幫助到大家,謝謝大家對(duì)本站的支持!
相關(guān)文章
java——Byte類(lèi)/包裝類(lèi)的使用說(shuō)明
這篇文章主要介紹了java——Byte類(lèi)/包裝類(lèi)的使用說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-02-02
springboot+Quartz實(shí)現(xiàn)任務(wù)調(diào)度的示例代碼
本篇文章主要介紹了springboot + Quartz 實(shí)現(xiàn)任務(wù)調(diào)度的示例代碼,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-02-02
Java對(duì)象轉(zhuǎn)Json,關(guān)于@JSONField對(duì)象字段重命名和順序問(wèn)題
這篇文章主要介紹了Java對(duì)象轉(zhuǎn)Json,關(guān)于@JSONField對(duì)象字段重命名和順序問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-08-08
SpringBoot使用validation-api實(shí)現(xiàn)對(duì)枚舉類(lèi)參數(shù)校驗(yàn)的方法
這篇文章主要介紹了SpringBoot使用validation-api實(shí)現(xiàn)對(duì)枚舉類(lèi)參數(shù)校驗(yàn),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-11-11
mybatis(mybatis-plus)映射文件(XML文件)中特殊字符轉(zhuǎn)義的實(shí)現(xiàn)
XML 文件在解析時(shí)會(huì)將五種特殊字符進(jìn)行轉(zhuǎn)義,本文主要介紹了mybatis(mybatis-plus)映射文件(XML文件)中特殊字符轉(zhuǎn)義的實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的可以了解一下2023-12-12
Java中List轉(zhuǎn)Map的幾種常見(jiàn)方式與對(duì)比
JavaList轉(zhuǎn)Map是一個(gè)非常常用的技術(shù),對(duì)于Java開(kāi)發(fā)人員來(lái)講,掌握該技術(shù)可以幫助我們更加高效地操作List集合中的對(duì)象,這篇文章主要給大家介紹了關(guān)于Java中List轉(zhuǎn)Map的幾種常見(jiàn)方式與對(duì)比的相關(guān)資料,需要的朋友可以參考下2024-02-02

