迭代器模式
文章目录
迭代器模式是一种行为设计模式,让你能在不暴露集合底层表现形式(列表、 栈和树等)的情况下遍历集合中所有的元素。
一:问题的引入
集合是编程中最常使用的数据类型之一。尽管如此,集合只是一组对象的容器而已。
大部分集合使用简单列表存储元素。但有些集合还会使用栈、树、图和其他复杂的数据结构。
无论集合的构成方式如何,它都必须提供某种访问元素的方式,便于其他代码使用其中的元素。
集合应提供一种能够遍历元素的方式,且保证它不会周而复始地访问同一个元素。
如果你的集合基于列表,那么这项工作听上去仿佛很简单。
但如何遍历复杂数据结构 (例如树) 中的元素呢?
例如,今天你需要使用深度优先算法来遍历树结构,明天可能会需要广度优先算法;下周则可能会需要其他方式 (比如随机存取树中的元素)。
不断向集合中添加遍历算法会模糊其 “高效存储数据” 的主要职责。此外,有些算法可能是根据特定应用订制的,将其加入泛型集合类中会显得非常奇怪。
另一方面,使用多种集合的客户端代码可能并不关心存储数据的方式。不过由于集合提供不同的元素访问方式,你的代码将不得不与特定集合类进行耦合。
二:问题的解决方案
迭代器模式的主要思想是将集合的遍历行为抽取为单独的迭代器对象。
除实现自身算法外,迭代器还封装了遍历操作的所有细节,例如当前位置和末尾剩余元素的数量。多个迭代器可以在相互独立的情况下同时访问集合。
迭代器通常会提供一个获取集合元素的基本方法。客户端可不断调用该方法直至它不返回任何内容,这意味着迭代器已经遍历了所有元素。
所有迭代器必须实现相同的接口。这样一来,只要有合适的迭代器,客户端代码就能兼容任何类型的集合或遍历算法。
如果你需要采用特殊方式来遍历集合,只需创建一个新的迭代器类即可,无需对集合或客户端进行修改。
三:迭代器模式的结构
1:迭代器
迭代器接口声明了遍历集合所需的操作:获取下一个元素,获取当前位置和重新开始迭代等等方法的定义
2:具体迭代器
实现了遍历集合的一种特定的算法,迭代器对象必须跟踪自身遍历的进度,这使得多个迭代器可以相互独立的遍历同一个集合
3:集合
4:具体集合
四:应用场景
当集合背后为复杂的数据结构,且你希望对客户端隐藏其复杂性时(出于使用便利性或安全性的考虑),可以使用迭代器模式
迭代器封装了与复杂数据结构进行交互的细节,为客户端提供多个访问集合元素的简单方法。
这种方式不仅对客户端来说非常方便 而且能避免客户端在直接与集合交互时执行错误或有害的操作,从而起到保护集合的作用。
使用该模式可以减少程序中错误的遍历代码
重要迭代算法的代码往往体积非常庞大。
当这些代码被放置在程序业务逻辑中时,它会让原始代码的职责模糊不清,降低其可维护性。
因此,将遍历代码移到特定的迭代器中可使程序代码更加精炼和简洁。
如果你希望代码能够遍历不同的甚至是无法预知的数据结构,可以使用迭代器模式
该模式为集合和迭代器提供了一些通用接口。
如果你在代码中使用了这些接口,那么将其他实现了这些接口的集合和迭代器传递给它时,它仍将可以正常运行。
五:实现方式
- 声明迭代器接口。该接口必须提供至少一个方法来获取集合中的下个元素。 但为了使用方便,你还可以添加一些其他方法,例如获取前一个元素、记录当前位置和判断迭代是否已结束。
- 声明集合接口并描述一个获取迭代器的方法。其返回值必须是迭代器接口。如果你计划拥有多组不同的迭代器,则可以声明多个类似的方法。
- 为希望使用迭代器进行遍历的集合实现具体迭代器类。迭代器对象必须与单个集合实体链接。链接关系通常通过迭代器的构造函数建立。
- 在你的集合类中实现集合接口。 其主要思想是针对特定集合为客户端代码提供创建迭代器的快捷方式。 集合对象必须将自身传递给迭代器的构造函数来创建两者之间的链接。
- 检查客户端代码,使用迭代器替代所有集合遍历代码。每当客户端需要遍历集合元素时都会获取一个新的迭代器。
六:优缺点
优点
- 单一职责原则。 通过将体积庞大的遍历算法代码抽取为独立的类, 你可对客户端代码和集合进行整理。
- 开闭原则。 你可实现新型的集合和迭代器并将其传递给现有代码, 无需修改现有代码。
- 你可以并行遍历同一集合, 因为每个迭代器对象都包含其自身的遍历状态。
- 相似的, 你可以暂停遍历并在需要时继续。
缺点
- 如果你的程序只与简单的集合进行交互, 应用该模式可能会矫枉过正。
- 对于某些特殊集合, 使用迭代器可能比直接遍历的效率低。
七:与其他设计模式的关系
- 你可以使用迭代器模式来遍历组合模式树。
- 你可以同时使用工厂方法模式和迭代器来让子类集合返回不同类型的迭代器, 并使得迭代器与集合相匹配。
- 你可以同时使用备忘录模式和迭代器来获取当前迭代器的状态, 并且在需要的时候进行回滚。
- 可以同时使用访问者模式和迭代器来遍历复杂数据结构, 并对其中的元素执行所需操作, 即使这些元素所属的类完全不同。
八:代码示例
1:伪代码
在本例中, 迭代器模式用于遍历一个封装了访问微信好友关系功能的特殊集合。 该集合提供使用不同方式遍历档案资料的多个迭代器。
“好友 (friends)” 迭代器可用于遍历指定档案的好友。
“同事 (colleagues)” 迭代器也提供同样的功能, 但仅包括与目标用户在同一家公司工作的好友。
这两个迭代器都实现了同一个通用接口,客户端能在不了解认证和发送 REST 请求等实现细节的情况下获取档案。
客户端仅通过接口与集合和迭代器交互,也就不会同具体类耦合。
如果你决定将应用连接到全新的社交网络,只需提供新的集合和迭代器类即可,无需修改现有代码。
// 集合接口必须声明一个用于生成迭代器的工厂方法。如果程序中有不同类型的迭代器,你也可以声明多个方法。
interface SocialNetwork is
method createFriendsIterator(profileId):ProfileIterator
method createCoworkersIterator(profileId):ProfileIterator
// 每个具体集合都与其返回的一组具体迭代器相耦合。但客户并不是这样的,因为
// 这些方法的签名将会返回迭代器接口。
class WeChat implements SocialNetwork is
// ……大量的集合代码应该放在这里……
// 迭代器创建代码。
method createFriendsIterator(profileId) is
return new WeChatIterator(this, profileId, "friends")
method createCoworkersIterator(profileId) is
return new WeChatIterator(this, profileId, "coworkers")
// 所有迭代器的通用接口。
interface ProfileIterator is
method getNext():Profile
method hasMore():bool
// 具体迭代器类。
class WeChatIterator implements ProfileIterator is
// 迭代器需要一个指向其遍历集合的引用。
private field weChat: WeChat
private field profileId, type: string
// 迭代器对象会独立于其他迭代器来对集合进行遍历。因此它必须保存迭代器的状态。
private field currentPosition
private field cache: array of Profile
constructor WeChatIterator(weChat, profileId, type) is
this.weChat = weChat
this.profileId = profileId
this.type = type
private method lazyInit() is
if (cache == null)
cache = weChat.socialGraphRequest(profileId, type)
// 每个具体迭代器类都会自行实现通用迭代器接口。
method getNext() is
if (hasMore())
result = cache[currentPosition]
currentPosition++
return result
method hasMore() is
lazyInit()
return currentPosition < cache.length
// 这里还有一个有用的绝招:你可将迭代器传递给客户端类,无需让其拥有访问整个集合的权限。这样一来,你就无需将集合暴露给客户端了。
//
// 还有另一个好处:你可在运行时将不同的迭代器传递给客户端,从而改变客户端
// 与集合互动的方式。这一方法可行的原因是客户端代码并没有和具体迭代器类相耦合。
class SocialSpammer is
method send(iterator: ProfileIterator, message: string) is
while (iterator.hasMore())
profile = iterator.getNext()
System.sendEmail(profile.getEmail(), message)
// 应用程序(Application)类可对集合和迭代器进行配置,然后将其传递给客户
端代码。
class Application is
field network: SocialNetwork
field spammer: SocialSpammer
method config() is
if working with WeChat
this.network = new WeChat()
if working with LinkedIn
this.network = new LinkedIn()
this.spammer = new SocialSpammer()
method sendSpamToFriends(profile) is
iterator = network.createFriendsIterator(profile.getId())
spammer.send(iterator, "非常重要的消息")
method sendSpamToCoworkers(profile) is
iterator = network.createCoworkersIterator(profile.getId())
spammer.send(iterator, "非常重要的消息")
2:项目结构
3:代码实现
实体类
package com.cui.commonboot.design_pattern.action_pattern.iterator_pattern.social_networks;
import com.cui.commonboot.design_pattern.action_pattern.iterator_pattern.iterators.ProfileIterator;
/**
* <p>
* 功能描述:通用的社交网络接口
* </p>
*
* @author cui haida
* @date 2023/09/17/20:34
*/
public interface SocialNetwork {
ProfileIterator createFriendsIterator(String profileEmail);
ProfileIterator createCoworkersIterator(String profileEmail);
}
package com.cui.commonboot.design_pattern.action_pattern.iterator_pattern.profile;
import lombok.Getter;
import lombok.Setter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* <p>
* 功能描述:社交档案实体类
* </p>
*
* @author cui haida
* @date 2023/09/17/20:26
*/
@Setter
public class Profile {
/**
* 姓名
*/
private String name;
/**
* email
*/
private String email;
/**
* 联系人
*/
private Map<String, List<String>> contacts = new HashMap<>();
/**
* 构造函数
*/
public Profile(String name, String email, String... contacts) {
this.name = name;
this.email = email;
for (String contact : contacts) {
// 通过:拆分成为两个部分,前半部分是联系人类型,后半部分是联系人的email
String[] parts = contact.split(":");
String contactType = "friend";
String contactEmail;
if (parts.length == 1) {
contactEmail = parts[0];
} else {
contactType = parts[0];
contactEmail = parts[1];
}
// 加入
List<String> contactsOrDefault = this.contacts.getOrDefault(contactType, new ArrayList<>());
contactsOrDefault.add(contactEmail);
this.contacts.put(contactType, contactsOrDefault);
}
}
public String getEmail() {
return email;
}
public String getName() {
return name;
}
public List<String> getContacts(String contactType) {
return contacts.getOrDefault(contactType, new ArrayList<>());
}
}
package com.cui.commonboot.design_pattern.action_pattern.iterator_pattern.social_networks;
import com.cui.commonboot.design_pattern.action_pattern.iterator_pattern.iterators.FacebookIterator;
import com.cui.commonboot.design_pattern.action_pattern.iterator_pattern.iterators.ProfileIterator;
import com.cui.commonboot.design_pattern.action_pattern.iterator_pattern.profile.Profile;
import java.util.ArrayList;
import java.util.List;
/**
* <p>
* 功能描述:社交网络facebook
* </p>
*
* @author cui haida
* @date 2023/09/17/20:35
*/
public class Facebook implements SocialNetwork {
/**
* 社交档案列表
*/
private List<Profile> profiles;
/**
* 构造函数
* @param cache 缓存
*/
public Facebook(List<Profile> cache) {
this.profiles = cache == null ? new ArrayList<>() : cache;
}
/**
* 从facebook中请求档案
* @param profileEmail 档案邮箱
* @return 结果
*/
public Profile requestProfileFromFacebook(String profileEmail) {
// 模拟网络延迟
simulateNetworkLatency();
System.out.println("Facebook: Loading profile '" + profileEmail + "' over the network...");
// 返回找到的facebook的数据
return findProfile(profileEmail);
}
/**
* 从facebook中找到档案的伙伴
* @param profileEmail email
* @param contactType 类型
* @return 对应用户的伙伴
*/
public List<String> requestProfileFriendsFromFacebook(String profileEmail, String contactType) {
// 模拟网络延迟
simulateNetworkLatency();
System.out.println("Facebook: Loading '" + contactType + "' list of '" + profileEmail + "' over the network...");
// 先找到这个用户
Profile profile = findProfile(profileEmail);
if (profile != null) {
// 如果找到了对应的用户,通过type找到用户的所有的联系人
return profile.getContacts(contactType);
}
return null;
}
/**
* 通过email找到指定的用户
* @param profileEmail email
* @return 用户
*/
private Profile findProfile(String profileEmail) {
for (Profile profile : profiles) {
if (profile.getEmail().equals(profileEmail)) {
return profile;
}
}
return null;
}
/**
* 模拟网络延迟
*/
private void simulateNetworkLatency() {
try {
Thread.sleep(2500);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
@Override
public ProfileIterator createFriendsIterator(String profileEmail) {
return new FacebookIterator(this, "friends", profileEmail);
}
@Override
public ProfileIterator createCoworkersIterator(String profileEmail) {
return new FacebookIterator(this, "coworkers", profileEmail);
}
}
package com.cui.commonboot.design_pattern.action_pattern.iterator_pattern.social_networks;
import com.cui.commonboot.design_pattern.action_pattern.iterator_pattern.iterators.LinkedInIterator;
import com.cui.commonboot.design_pattern.action_pattern.iterator_pattern.iterators.ProfileIterator;
import com.cui.commonboot.design_pattern.action_pattern.iterator_pattern.profile.Profile;
import java.util.ArrayList;
import java.util.List;
/**
* <p>
* 功能描述:linkedIn设计网络
* </p>
*
* @author cui haida
* @date 2023/09/17/20:36
*/
public class LinkedIn implements SocialNetwork {
// 社交档案集合
private List<Profile> contacts;
/**
* 构造函数
* @param cache 缓存
*/
public LinkedIn(List<Profile> cache) {
this.contacts = cache == null ? new ArrayList<>() : cache;
}
public Profile requestContactInfoFromLinkedInAPI(String profileEmail) {
simulateNetworkLatency();
System.out.println("LinkedIn: Loading profile '" + profileEmail + "' over the network...");
// ...and return test data.
return findContact(profileEmail);
}
public List<String> requestRelatedContactsFromLinkedInAPI(String profileEmail, String contactType) {
simulateNetworkLatency();
System.out.println("LinkedIn: Loading '" + contactType + "' list of '" + profileEmail + "' over the network...");
// ...and return test data.
Profile profile = findContact(profileEmail);
if (profile != null) {
return profile.getContacts(contactType);
}
return null;
}
private Profile findContact(String profileEmail) {
for (Profile profile : contacts) {
if (profile.getEmail().equals(profileEmail)) {
return profile;
}
}
return null;
}
private void simulateNetworkLatency() {
try {
Thread.sleep(2500);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
@Override
public ProfileIterator createFriendsIterator(String profileEmail) {
return new LinkedInIterator(this, "friends", profileEmail);
}
@Override
public ProfileIterator createCoworkersIterator(String profileEmail) {
return new LinkedInIterator(this, "coworkers", profileEmail);
}
}
迭代器
package com.cui.commonboot.design_pattern.action_pattern.iterator_pattern.iterators;
import com.cui.commonboot.design_pattern.action_pattern.iterator_pattern.profile.Profile;
/**
* <p>
* 功能描述:定义档案接口
* </p>
*
* @author cui haida
* @date 2023/09/17/20:25
*/
public interface ProfileIterator {
/**
* 是否有下一个
*
* @return 存在 -> true, 不存在 -> false
*/
boolean hasNext();
/**
* 拿到下一个
*
* @return 下一个档案
*/
Profile getNext();
/**
* 重置
*/
void reset();
}
package com.cui.commonboot.design_pattern.action_pattern.iterator_pattern.iterators;
import com.cui.commonboot.design_pattern.action_pattern.iterator_pattern.profile.Profile;
import com.cui.commonboot.design_pattern.action_pattern.iterator_pattern.social_networks.Facebook;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
/**
* <p>
* 功能描述:
* </p>
*
* @author cui haida
* @date 2023/09/17/20:33
*/
public class FacebookIterator implements ProfileIterator {
private Facebook facebook;
private String type;
private String email;
private int currentPosition = 0;
private List<String> emails = new ArrayList<>();
private List<Profile> profiles = new ArrayList<>();
public FacebookIterator(Facebook facebook, String type, String email) {
this.facebook = facebook;
this.type = type;
this.email = email;
}
/**
* 延迟加载
*/
private void lazyLoad() {
if (emails.size() == 0) {
List<String> profiles = facebook.requestProfileFriendsFromFacebook(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 getNext() {
if (!hasNext()) {
return null;
}
String friendEmail = emails.get(currentPosition);
Profile friendProfile = profiles.get(currentPosition);
if (friendProfile == null) {
friendProfile = facebook.requestProfileFromFacebook(friendEmail);
profiles.set(currentPosition, friendProfile);
}
currentPosition++;
return friendProfile;
}
/**
* 游标归位到0
*/
@Override
public void reset() {
currentPosition = 0;
}
}
package com.cui.commonboot.design_pattern.action_pattern.iterator_pattern.iterators;
import com.cui.commonboot.design_pattern.action_pattern.iterator_pattern.profile.Profile;
import com.cui.commonboot.design_pattern.action_pattern.iterator_pattern.social_networks.LinkedIn;
import java.util.ArrayList;
import java.util.List;
/**
* <p>
* 功能描述:
* </p>
*
* @author cui haida
* @date 2023/09/17/20:37
*/
public class LinkedInIterator implements ProfileIterator {
private LinkedIn linkedIn;
private String type;
private String email;
private int currentPosition = 0;
private List<String> emails = new ArrayList<>();
private List<Profile> contacts = new ArrayList<>();
public LinkedInIterator(LinkedIn linkedIn, String type, String email) {
this.linkedIn = linkedIn;
this.type = type;
this.email = email;
}
private void lazyLoad() {
if (emails.size() == 0) {
List<String> profiles = linkedIn.requestRelatedContactsFromLinkedInAPI(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 getNext() {
if (!hasNext()) {
return null;
}
String friendEmail = emails.get(currentPosition);
Profile friendContact = contacts.get(currentPosition);
if (friendContact == null) {
friendContact = linkedIn.requestContactInfoFromLinkedInAPI(friendEmail);
contacts.set(currentPosition, friendContact);
}
currentPosition++;
return friendContact;
}
@Override
public void reset() {
currentPosition = 0;
}
}
应用
package com.cui.commonboot.design_pattern.action_pattern.iterator_pattern.spammer;
import com.cui.commonboot.design_pattern.action_pattern.iterator_pattern.iterators.ProfileIterator;
import com.cui.commonboot.design_pattern.action_pattern.iterator_pattern.profile.Profile;
import com.cui.commonboot.design_pattern.action_pattern.iterator_pattern.social_networks.SocialNetwork;
/**
* <p>
* 功能描述:消息发送应用
* </p>
*
* @author cui haida
* @date 2023/09/17/20:38
*/
public class SocialSpammer {
public SocialNetwork network;
public ProfileIterator iterator;
public SocialSpammer(SocialNetwork network) {
this.network = network;
}
/**
* 发送消息给朋友
* @param profileEmail email
* @param message 消息内容
*/
public void sendSpamToFriends(String profileEmail, String message) {
System.out.println("\nIterating over friends...\n");
iterator = network.createFriendsIterator(profileEmail);
while (iterator.hasNext()) {
Profile profile = iterator.getNext();
sendMessage(profile.getEmail(), message);
}
}
/**
* 发送消息给同事
* @param profileEmail email
* @param message 消息内容
*/
public void sendSpamToCoworkers(String profileEmail, String message) {
System.out.println("\nIterating over coworkers...\n");
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("Sent message to: '" + email + "'. Message body: '" + message + "'");
}
}
测试类
package com.cui.commonboot.design_pattern.action_pattern.iterator_pattern;
import com.cui.commonboot.design_pattern.action_pattern.iterator_pattern.profile.Profile;
import com.cui.commonboot.design_pattern.action_pattern.iterator_pattern.social_networks.Facebook;
import com.cui.commonboot.design_pattern.action_pattern.iterator_pattern.social_networks.LinkedIn;
import com.cui.commonboot.design_pattern.action_pattern.iterator_pattern.social_networks.SocialNetwork;
import com.cui.commonboot.design_pattern.action_pattern.iterator_pattern.spammer.SocialSpammer;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
/**
* <p>
* 功能描述:测试类
* </p>
*
* @author cui haida
* @date 2023/09/17/20:24
*/
public class Demo {
public static Scanner scanner = new Scanner(System.in);
public static void main(String[] args) {
System.out.println("Please specify social network to target spam tool (default:Facebook):");
System.out.println("1. Facebook");
System.out.println("2. LinkedIn");
String choice = scanner.nextLine();
SocialNetwork network;
if (choice.equals("2")) {
network = new LinkedIn(createTestProfiles());
}
else {
network = new Facebook(createTestProfiles());
}
SocialSpammer spammer = new SocialSpammer(network);
spammer.sendSpamToFriends("anna.smith@bing.com",
"Hey! This is Anna's friend Josh. Can you do me a favor and like this post [link]?");
spammer.sendSpamToCoworkers("anna.smith@bing.com",
"Hey! This is Anna's boss Jason. Anna told me you would be interested in [link].");
}
public static List<Profile> createTestProfiles() {
List<Profile> data = new ArrayList<Profile>();
data.add(new Profile("anna.smith@bing.com", "Anna Smith", "friends:mad_max@ya.com", "friends:catwoman@yahoo.com", "coworkers:sam@amazon.com"));
data.add(new Profile("mad_max@ya.com", "Maximilian", "friends:anna.smith@bing.com", "coworkers:sam@amazon.com"));
data.add(new Profile("bill@microsoft.eu", "Billie", "coworkers:avanger@ukr.net"));
data.add(new Profile("avanger@ukr.net", "John Day", "coworkers:bill@microsoft.eu"));
data.add(new Profile("sam@amazon.com", "Sam Kitting", "coworkers:anna.smith@bing.com", "coworkers:mad_max@ya.com", "friends:catwoman@yahoo.com"));
data.add(new Profile("catwoman@yahoo.com", "Liza", "friends:anna.smith@bing.com", "friends:sam@amazon.com"));
return data;
}
}