行为型模式:迭代器模式

LieBrother原文
行为型模式:迭代器模式

景

十一大行为型模式之六:迭代器模式。

简介

姓名 :迭代器模式

英文名 :Iterator Pattern

价值观 :人生没有回头路

个人介绍

Provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation.
它提供一种方法访问一个容器对象中各个元素,而又不需暴露该对象的内部细节。
(来自《设计模式之禅》)

你要的故事

大家伙听歌频率高么?是不是经常听歌曲来放松心情?我是经常会听歌,心情不好的时候听歌,心情好的时候也听歌。。。今天讲的迭代器模式,我们就拿听歌这件事来说说,大家都知道听歌有几种模式:单曲循环、列表循环、随机等等。。。现在网易云音乐还多了一个心动模式。

既然说到迭代器模式,那这里就要着重讲讲列表循环这个听歌模式,其他的就先抛到脑后。在列表循环中,歌曲从第一条播放到最后一条,也就是一个遍历歌单的过程。我们有 2 种实现方式,一种是没有迭代器,通过获取歌单,用 for 循环遍历每一个歌曲,然后播放;另外一种是使用迭代器,获取歌单的一个迭代器,通过迭代器来遍历每一个歌曲,然后播放。下面我们就用代码来实现这 2 种方式。

木有迭代器

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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
public class NoIteratorTest {

public static void main(String[] args) {
NetEaseMusic1 netEaseMusic1 = new NetEaseMusic1();
netEaseMusic1.listenToMusicByLoop();
}
}

/**
* 网易云音乐
*/
class NetEaseMusic1 {

private IList1 songList;

public NetEaseMusic1() {
songList = new SongList1(3);
songList.add(new Song("让我留在你身边", "陈奕迅"));
songList.add(new Song("你曾是少年", "SHE"));
songList.add(new Song("Perfect", "Ed Sheeran"));
}

/**
* 列表循环
*/
public void listenToMusicByLoop() {
for (int i = 0; i < songList.size(); i++) {
System.out.println("听歌:" + ((ISong)songList.get(i)).getSongInfo());
}
}

}

/**
* 容器接口
*/
interface IList1 {

void add(Object object);

Object get(int index);

int size();
}

/**
* 歌单
*/
class SongList1 implements IList1 {

private ISong[] songs;
private int index;
private int size;

public SongList1(int size) {
songs = new ISong[size];
index = 0;
size = 0;
}

@Override
public void add(Object object) {
songs[index++] = (ISong) object;
size ++;
}

@Override
public Object get(int index) {
if (index < size) {
return songs[index];
}
return null;
}

@Override
public int size() {
return size;
}
}


/**
* 歌曲接口
*/
interface ISong {
String getSongInfo();
}

/**
* 歌曲
*/
class Song implements ISong{

private String name;
private String singer;

public Song(String name, String singer) {
this.name = name;
this.singer = singer;
}

@Override
public String getSongInfo() {
return this.name + "--" + this.singer;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getSinger() {
return singer;
}

public void setSinger(String singer) {
this.singer = singer;
}

}

打印结果:
听歌:让我留在你身边--陈奕迅
听歌:你曾是少年--SHE
听歌:Perfect--Ed Sheeran

我们定义了 ISong 接口,里面有个 getSongInfo() 方法来获取歌曲信息,用 Song 类来定义歌曲。没有用 Java 自带的容器来存放歌曲,这里实现了一个自定义容器接口 IList1,定义 SongList1 来做歌曲的容器,为什么不用 Java 自带的 ArrayList 等等?因为 Java 自带的已经实现了迭代器功能了,我们这里自定义其实就是在模仿自带的容器的实现。NetEaseMusic1 类是充当网易云音乐客户端,在 listenToMusicByLoop() 方法中,我们可以看到是获取了歌单 songList,然后一个一个遍历,这是没有使用迭代器的代码。

下面看一下使用迭代器的代码是怎么样的。

用迭代器实现遍历

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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
public class IteratorTest {

public static void main(String[] args) {
NetEaseMusic2 netEaseMusic2 = new NetEaseMusic2();
netEaseMusic2.listenToMusicByLoop();
}

}

/**
* 网易云音乐
*/
class NetEaseMusic2{

private IList2 songList;

public NetEaseMusic2() {
songList = new SongList2(3);
songList.add(new Song("让我留在你身边", "陈奕迅"));
songList.add(new Song("你曾是少年", "SHE"));
songList.add(new Song("Perfect", "Ed Sheeran"));
}

/**
* 列表循环
*/
public void listenToMusicByLoop() {
IIterator iterator = songList.iterator();
while (iterator.hasNext()) {
System.out.println("听歌:" + ((ISong)iterator.next()).getSongInfo());
}

}

}

/**
* 容器接口
*/
interface IList2 {

IIterator iterator();

void add(Object object);

Object get(int index);

int size();
}

/**
* 歌单
*/
class SongList2 implements IList2 {

private ISong[] songs;
private int index;
private int size;

public SongList2(int size) {
songs = new ISong[size];
index = 0;
size = 0;
}

@Override
public IIterator iterator() {
return new IteratorImpl(this);
}

@Override
public void add(Object object) {
songs[index++] = (ISong) object;
size ++;
}

@Override
public Object get(int index) {
if (index < size) {
return songs[index];
}
return null;
}

@Override
public int size() {
return size;
}
}


/**
* 迭代器
*/
interface IIterator {
Object next();
boolean hasNext();
}

/**
* 迭代器实现类
*/
class IteratorImpl implements IIterator {

private IList2 list;
private int index;

public IteratorImpl(IList2 list) {
this.list = list;
this.index = 0;
}

@Override
public Object next() {
return list.get(index++);
}

@Override
public boolean hasNext() {
if (index < list.size()) {
return true;
}
return false;
}
}

打印结果:
听歌:让我留在你身边--陈奕迅
听歌:你曾是少年--SHE
听歌:Perfect--Ed Sheeran

代码中我们自定义了一个迭代器接口 IIterator 和迭代器具体实现类 IteratorImpl,有关键的 2 个方法,hasNext() 判断是否有存在下一个元素,next() 获取下一个元素。而 IList2 接口则比 IList1 接口多了一个获取迭代器的方法 iterator(),这让网易云音乐在遍历歌单的时候,不用直接使用 songList 来遍历,而可以通过 songList.iterator() 获取迭代器来实现遍历的过程。NetEaseMusic2.listenToMusicByLoop() 这个方法里面就直接获取迭代器来遍历了。

代码:
Iterator Pattern

总结

迭代器模式是所有设计模式中使用最广泛的,有不少开发同学知道迭代器,但是不知道它是设计模式的。虽然迭代器的代码会比没有迭代器的代码复杂,但是加上迭代器可以让容器有统一的遍历代码风格,不用各自去实现遍历方法,有更好的封装性。在 Java 中,迭代器已经运用很广泛,比如 Java 中访问 MySQL 获取数据就是用迭代器来遍历数据的。好了,迭代器模式就讲到这,大家知道的知识就不多说啦。

参考资料:《大话设计模式》、《设计模式之禅》

推荐阅读:

行为型模式:策略模式

行为型模式:责任链模式

行为型模式:命令模式

希望文章对您有所帮助,设计模式系列会持续更新,感兴趣的同学可以关注公众号:LieBrother,第一时间获取文章推送阅读,也可以一起交流,交个朋友。

公众号