迭代器模式(行为型模式)
原创大约 5 分钟
模式概述
迭代器模式是一种行为设计模式,它可以在不暴露集合底层数据结构,例如,列表、 栈和树等的情况下遍历集合中所有的元素。
问题
集合是编程中最常使用的数据类型,也只是一组对象的容器。大部分集合使用简单的列表存储元素,但有些集合还会使用栈、树、图和其他复杂的数据结构。
无论是哪种集合,一方面,它都必须提供某种访问元素的方式,且保证它不会周而复始地访问同一个元素。
另一方面,使用多种集合的客户端代码并不知道存储数据的方式,因此代码不得不与特定的集合类耦合,以便实现对元素的访问。
方案
迭代器模式的主要思想是将集合的遍历行为封装到单独的迭代器对象中。
迭代器封装了遍历集合操作的所有细节,例如当前位置和末尾剩余元素的数量,而且多个迭代器可以在相互独立的情况下同时访问集合。
结构

实现
Java
/**
* 迭代器接口
*
*/
public interface Iterator {
boolean hasNext();
Profile next();
void reset();
}
/**
* 在微博上迭代实现查找朋友资料
*
*/
public class WeiboIterator implements Iterator {
private Weibo weibo;
private String type;
private String email;
private int currentPosition = 0;
private List<String> emails = new ArrayList<>();
private List<Profile> profiles = new ArrayList<>();
public WeiboIterator(Weibo weibo, String type, String email) {
this.weibo = weibo;
this.type = type;
this.email = email;
}
private void lazyLoad() {
if (emails.size() == 0) {
List<String> profiles = weibo.requestProfileFriendsFromWeibo(this.email, this.type);
for (String profile : profiles) {
this.emails.add(profile);
this.profiles.add(null);
}
}
}
@Override
public boolean hasNext() {
lazyLoad();
return currentPosition < emails.size();
}
@Override
public Profile next() {
if (!hasNext()) {
return null;
}
String friendEmail = emails.get(currentPosition);
Profile friendProfile = profiles.get(currentPosition);
if (friendProfile == null) {
friendProfile = facebook.requestProfileFromWeibo(friendEmail);
profiles.set(currentPosition, friendProfile);
}
currentPosition++;
return friendProfile;
}
@Override
public void reset() {
currentPosition = 0;
}
}
/**
* 在微信上迭代实现查找朋友资料
*
*/
public class WechatIterator implements Iterator {
private Wechat wechat;
private String type;
private String email;
private int currentPosition = 0;
private List<String> emails = new ArrayList<>();
private List<Profile> contacts = new ArrayList<>();
public WechatIterator(Wechat wechat, String type, String email) {
this.wechat = wechat;
this.type = type;
this.email = email;
}
private void lazyLoad() {
if (emails.size() == 0) {
List<String> profiles = wechat.requestContactsFromWechat(this.email, this.type);
for (String profile : profiles) {
this.emails.add(profile);
this.contacts.add(null);
}
}
}
@Override
public boolean hasNext() {
lazyLoad();
return currentPosition < emails.size();
}
@Override
public Profile next() {
if (!hasNext()) {
return null;
}
String friendEmail = emails.get(currentPosition);
Profile friendContact = contacts.get(currentPosition);
if (friendContact == null) {
friendContact = linkedIn.requestContactInfoFromWechat(friendEmail);
contacts.set(currentPosition, friendContact);
}
currentPosition++;
return friendContact;
}
@Override
public void reset() {
currentPosition = 0;
}
}
/**
* 社交网络接口
*
*/
public interface SocialNetwork {
Iterator createFriendsIterator(String profileEmail);
Iterator createCoworkersIterator(String profileEmail);
}
/**
* 微博
*
*/
public class Weibo implements SocialNetwork {
private List<Profile> profiles;
public Weibo(List<Profile> cache) {
if (cache != null) {
this.profiles = cache;
} else {
this.profiles = new ArrayList<>();
}
}
public Profile requestProfileFromWeibo(String profileEmail) {
System.out.println("从微博获取资料");
return new Profile();
}
public List<String> requestProfileFriendsFromWeibo(String profileEmail, String contactType) {
System.out.println("从微博获取朋友资料");
return null;
}
@Override
public Iterator createFriendsIterator(String profileEmail) {
return new WeiboIterator(this, "friends", profileEmail);
}
@Override
public Iterator createCoworkersIterator(String profileEmail) {
return new WeiboIterator(this, "coworkers", profileEmail);
}
}
/**
* 微信
*
*/
public class Wechat implements SocialNetwork {
private List<Profile> contacts;
public Wechat(List<Profile> cache) {
if (cache != null) {
this.contacts = cache;
} else {
this.contacts = new ArrayList<>();
}
}
public Profile requestContactInfoFromWechat(String profileEmail) {
System.out.println("从微信获取资料");
return new Profile();
}
public List<String> requestRelatedContactsFromWechat(String profileEmail, String contactType) {
System.out.println("从微信获取朋友资料");
return null;
}
@Override
public Iterator createFriendsIterator(String profileEmail) {
return new WechatIterator(this, "friends", profileEmail);
}
@Override
public Iterator createCoworkersIterator(String profileEmail) {
return new WechatIterator(this, "coworkers", profileEmail);
}
}
/**
* 个人资料
*
*/
public class Profile {
private String name;
private String email;
private Map<String, List<String>> contacts = new HashMap<>();
public Profile(String email, String name, String... contacts) {
this.email = email;
this.name = name;
for (String contact : contacts) {
String[] parts = contact.split(":");
String contactType = "friend", contactEmail;
if (parts.length == 1) {
contactEmail = parts[0];
}
else {
contactType = parts[0];
contactEmail = parts[1];
}
if (!this.contacts.containsKey(contactType)) {
this.contacts.put(contactType, new ArrayList<>());
}
this.contacts.get(contactType).add(contactEmail);
}
}
}
/**
* 消息发送应用
*
*/
public class SocialSpammer {
public SocialNetwork network;
public Iterator iterator;
public SocialSpammer(SocialNetwork network) {
this.network = network;
}
public void sendSpamToFriends(String profileEmail, String message) {
iterator = network.createFriendsIterator(profileEmail);
while (iterator.hasNext()) {
Profile profile = iterator.getNext();
sendMessage(profile.getEmail(), message);
}
}
public void sendSpamToCoworkers(String profileEmail, String message) {
iterator = network.createCoworkersIterator(profileEmail);
while (iterator.hasNext()) {
Profile profile = iterator.getNext();
sendMessage(profile.getEmail(), message);
}
}
public void sendMessage(String email, String message) {
System.out.println("发送消息");
}
}
/**
* 客户端
*
*/
public class Demo {
public static Scanner scanner = new Scanner(System.in);
public static void main(String[] args) {
System.out.println("指定要发送的消息:");
System.out.println("1. Weibo");
System.out.println("2. Wechat");
String choice = scanner.nextLine();
SocialNetwork network;
if (choice.equals("2")) {
network = new Wechat(createTestProfiles());
}
else {
network = new Weibo(createTestProfiles());
}
SocialSpammer spammer = new SocialSpammer(network);
spammer.sendSpamToFriends("123@qq.com", "[link]?");
spammer.sendSpamToCoworkers("123@qq.com", [link].");
}
public static List<Profile> createTestProfiles() {
List<Profile> data = new ArrayList<Profile>();
data.add(new Profile("123@qq.com", "test1"));
data.add(new Profile("456@qq.com", "test2"));
return data;
}
}
Go
package main
import "fmt"
/**
* 集合接口
*
*/
type Collection interface {
createIterator() Iterator
}
/**
* 具体集合
*
*/
type User struct {
name string
age int
}
type UserCollection struct {
users []*User
}
func (u *UserCollection) createIterator() Iterator {
return &UserIterator{
users: u.users,
}
}
/**
* 迭代器接口
*
*/
type Iterator interface {
hasNext() bool
getNext() *User
}
/**
* 具体迭代器
*
*/
type UserIterator struct {
index int
users []*User
}
func (u *UserIterator) hasNext() bool {
if u.index < len(u.users) {
return true
}
return false
}
func (u *UserIterator) getNext() *User {
if u.hasNext() {
user := u.users[u.index]
u.index++
return user
}
return nil
}
/**
* 客户端
*
*/
func main() {
user1 := &User{
name: "a",
age: 30,
}
user2 := &User{
name: "b",
age: 20,
}
userCollection := &UserCollection{
users: []*User{user1, user2},
}
iterator := userCollection.createIterator()
for iterator.hasNext() {
user := iterator.getNext()
fmt.Printf("用户是 %+v\n", user)
}
}
适用场景
当集合的数据结构复杂且希望对客户端隐藏其复杂性时,可以使用迭代器模式。
如果希望代码能够遍历不同的甚至是无法预知的数据结构时,可以使用迭代器模式。
优缺点
迭代器模式的优点。
将体积庞大的遍历算法代码抽取为独立的类,符合
单一职责原则
。可实现新型的集合和迭代器而无需修改现有代码,符合
开闭原则
。可以并行遍历同一集合,因为每个迭代器对象都包含其自身的遍历状态。
迭代器模式的缺点。
如果程序只与简单的集合进行交互的话,应用该模式会矫枉过正。
对于某些特殊集合,使用迭代器可能比直接遍历的效率更低。
相关性
可以使用
迭代器模式
来遍历组合模式
构建的树。可以同时使用
工厂方法模式
和迭代模式
来让子类集合返回不同类型的迭代器,并使得迭代器与集合相匹配。可以同时使用
备忘录模式
和迭代器模式
来获取当前迭代器的状态,并在需要的时候回滚。可以同时使用
访问者模式
和迭代器模式
来遍历复杂数据结构,并对其中的元素执行所需操作,即使这些元素所属的类完全不同。
感谢支持
更多内容,请移步《超级个体》。