【设计模式】迭代器模式

博主声明:

转载请在开头附加本文链接及作者信息,并标记为转载。本文由博主 威威喵 原创,请多支持与指教。

本文首发于此   博主威威喵  |  博客主页https://blog.csdn.net/smile_running

    迭代器模式(Iterator Pattern),它的作用是:提供一种方法顺序访问一个聚合对象中各个元素, 而又无须暴露该对象的内部表示。

    以上是官方用语,就一句话概况了一个模式,你能多说几个字吗?咱也不明白你到底表达什么意思啊。好吧,我们用自己的话来阐述一下,比如有一个数据集合,数据内部存储方式可以是数组、可以是列表、可以是队列、栈等容器,我们可以顺序的遍历这个数据集合中的内容,而并不知道它内部存储的方式

    好像有那么点明白了,来看一个最普遍的例子,几乎每个人都使用过,那就是集合的遍历,除了常见的几种循环来遍历,还有一种是以迭代器的方式来遍历,比如这样的:

Iterator<Person> it = personList.iterator();
while (it.hasNext()) {
    Person person = it.next();
    String name = person.getName();
    int salary = person.getSalary();
    int age = person.getAge();
}

    这种方式很明显就用了迭代器来遍历的,当它调用 next() 方法时,它的 index++ 就可以取出下一个数据元素。既然这样,我们就模仿着源码来写一个迭代器模式吧,不过我们的例子使用的当然不是遍历这么简单了,来搞一个登录检测系统,比如有这样一个登录模块,登录账号可以是 QQ 也可以是 微信,所以我们要去遍历 QQ 和微信这两个系统的用户信息,然后看哪一个系统有我们的用户数据,如果匹配成功的话, 那就可以登录了,那开始吧。

首先,是我们的 QQ 用户信息系统,我们用数组的方式存储数据,代码如下:

package com.xww.dp.iterator;

public class QQSystem {

	private User[] userInfos = null;

	public QQSystem() {
		User usr1 = new User("user01", "12345");
		User usr2 = new User("user04", "12345");
		User usr3 = new User("user03", "12345");
		userInfos = new User[] { usr1, usr2, usr3 };
	}

	public User[] getUserInfos() {
		return userInfos;
	}
}

还有一个是微信的用户信息系统,为了区别 QQ,所以用了 List 来存储数据,代码如下:

package com.xww.dp.iterator;

import java.util.ArrayList;
import java.util.List;

public class WeChatSystem {

	private List<User> userInfos = null;

	public WeChatSystem() {
		userInfos = new ArrayList<User>();
		userInfos.add(new User("user04", "12345"));
		userInfos.add(new User("user05", "12345"));
		userInfos.add(new User("user06", "12345"));
	}

	public List<User> getUserInfos() {
		return userInfos;
	}
}

还有最简单的一个是用户实体类,不用多说了吧,代码:

package com.xww.dp.iterator;

public class User {

	private String usrName;

	private String usrPwd;

	public User(String usrName, String usrPwd) {
		super();
		this.usrName = usrName;
		this.usrPwd = usrPwd;
	}

	public String getUsrName() {
		return usrName;
	}

	public void setUsrName(String usrName) {
		this.usrName = usrName;
	}

	public String getUsrPwd() {
		return usrPwd;
	}

	public void setUsrPwd(String usrPwd) {
		this.usrPwd = usrPwd;
	}

}

    最后,我们的登录客户端,为了判断我们的用户信息准确性,先从 QQ 信息系统中匹配,如果没有匹配到,再从微信信息系统中匹配,若有一次匹配成功,便能够登录,否则登录失败,代码逻辑是这样的,具体代码如下:
 

package com.xww.dp.iterator;

import java.util.List;

/**
 * 迭代器模式
 * 
 * @author xww
 * @设计模式 https://blog.csdn.net/smile_Running/column/info/27169
 */
public class IteratorPatternClient {

	public static void main(String[] args) {

		boolean isLoginSucces = checkQQInfos("user05", "12345");

		if (!isLoginSucces) {
			isLoginSucces = checkWechatInfos("user05", "12345");
		}

		if (isLoginSucces) {
			System.out.println("登录成功");
		} else {
			System.out.println("登录失败");
		}
	}

	public static boolean checkQQInfos(String usrName, String usrPwd) {
		QQSystem qq = new QQSystem();
		User[] userInfos = qq.getUserInfos();
		for (User usr : userInfos) {
			if (usr.getUsrName().equals(usrName)
					&& usr.getUsrPwd().equals(usrPwd)) {
				return true;
			}
		}
		return false;
	}

	public static boolean checkWechatInfos(String usrName, String usrPwd) {
		WeChatSystem weChat = new WeChatSystem();
		List<User> userInfos = weChat.getUserInfos();
		for (User usr : userInfos) {
			if (usr.getUsrName().equals(usrName)
					&& usr.getUsrPwd().equals(usrPwd)) {
				return true;
			}
		}
		return false;
	}

}

    好了,上面代码就是我们中规中矩的没有任何设计模式所写的代码,看起来还是没什么毛病的,不过接下来听我给你分析一下,那你就知道有什么地方需要修改了。

    首先是我们 Main 方法中的逻辑判断问题,如果我们的用户信息系统很多,比如在 QQ 微信基础上加了 微博、百度、Github等等用户系统,那岂不是又得加几个 if else 语句。不光是 if else 语句的问题,而且下面对应的每一个方法都得添加,那岂不是非常麻烦的事情,而且代码好像也都差不多,不是就有重复代码了吗。

    经过这样一番分析,我们得出以上的几个缺点,因为我们每个 checkInfos 方法都是遍历信息,所以根据这种情况,我们引入迭代器模式是最为恰当的,下面进行修改我们的代码:

模仿我们开头提到的的数组迭代器的遍历代码,我们新建了一个 迭代器 接口:

package com.xww.dp.iterator;

public interface Iterator<T> {

	T next();

	boolean hasNext();
}

    接口中的 next() 表示数据向后移动一位,取得下一条数据元素。 hasNext() 表示判断有没有下一条数据。因为数据类型我们不知道,所以传入一个泛型最合适了。

    定义完迭代器接口以后,肯定是要实现它的,不过这里我们不能直接让 QQ 实现它,那样的会迭代器会有一点问题,所以我们要新建一个 QQ 迭代器的实现类,处理了 next() 和 hasNext() 的逻辑:

package com.xww.dp.iterator;

/**
 * QQ 迭代器类
 * 
 * @author xww
 *
 */
public class QQIterator implements Iterator<User> {
	private int index = 0;
	private User[] userInfos;

	public QQIterator(User[] userInfos) {
		this.userInfos = userInfos;
	}

	@Override
	public User next() {
		return userInfos[index++];
	}

	@Override
	public boolean hasNext() {
		return index < userInfos.length;
	}

}

 还有一个微信迭代器实现类以一样,不过它们的存储数据的容器不要搞错了:

package com.xww.dp.iterator;

import java.util.ArrayList;
import java.util.List;

public class WeChatSystem implements Iterator<User> {

	private List<User> userInfos = null;
	private int index = 0;

	public WeChatSystem() {
		userInfos = new ArrayList<User>();
		userInfos.add(new User("user04", "12345"));
		userInfos.add(new User("user05", "12345"));
		userInfos.add(new User("user06", "12345"));
	}

	@Override
	public User next() {
		return userInfos.get(index++);
	}

	@Override
	public boolean hasNext() {
		return index < userInfos.size();
	}
}

    由于迭代器模式它的定义上说,不能暴露内部的数据存储方式,所以从某种方式上,我们不能通过 get 来获取,所以还是参照开头的代码,发现有一个 iterator() 方法没用上,这应该是一个泛型接口,又因为要使用到 next() 、hasNext() 方法,那就要返回一个 Iterator<T> 接口了:

package com.xww.dp.iterator;

/**
 * 
 * @author xww https://blog.csdn.net/smile_Running/column/info/27169
 * @param <T>
 *            这里的泛型,传入的是 User 实体类
 */
public interface SubIterator<T> {

	Iterator<T> iterator();

}

    然后,开始修改我们的 QQ 系统,这里要把 get 方法给去掉了,然后去实现我们上面添加的一个 SubIterator 接口,泛型当然就得传入 User 了,代码如下:

package com.xww.dp.iterator;

/**
 * 修改 qq 系统代码,移除 get 方法,使用 迭代器的形式获取容器类型
 * 
 * @author xww
 *
 */
public class QQSystem implements SubIterator<User> {

	private User[] userInfos = null;

	public QQSystem() {
		User usr1 = new User("user01", "12345");
		User usr2 = new User("user04", "12345");
		User usr3 = new User("user03", "12345");
		userInfos = new User[] { usr1, usr2, usr3 };
	}

	@Override
	public Iterator<User> iterator() {
		return new QQIterator(userInfos);
	}

}

当然了,微信信息也同样需要修改,修改方式差不多,应该很容易懂,代码如下:

package com.xww.dp.iterator;

import java.util.ArrayList;
import java.util.List;

public class WeChatSystem implements SubIterator<User> {

	private List<User> userInfos = null;

	public WeChatSystem() {
		userInfos = new ArrayList<User>();
		userInfos.add(new User("user04", "12345"));
		userInfos.add(new User("user05", "12345"));
		userInfos.add(new User("user06", "12345"));
	}

	@Override
	public Iterator<User> iterator() {
		return new WechatIterator(userInfos);
	}

}

好了,最后我们的登录客户端代码也做出相应的修改,如下:

package com.xww.dp.iterator;

/**
 * 迭代器模式
 * 
 * @author xww
 * @设计模式 https://blog.csdn.net/smile_Running/column/info/27169
 */
public class IteratorPatternClient {

	public static void main(String[] args) {

		boolean isLoginSucces = checkInfos("user05", "12345",
				new QQSystem().iterator());

		if (!isLoginSucces) {
			isLoginSucces = checkInfos("user05", "12345",
					new WeChatSystem().iterator());
		}

		if (isLoginSucces) {
			System.out.println("登录成功");
		} else {
			System.out.println("登录失败");
		}
	}

	public static boolean checkInfos(String usrName, String usrPwd,
			Iterator<User> qq) {

		while (qq.hasNext()) {
			User usr = qq.next();
			if (usr.getUsrName().equals(usrName)
					&& usr.getUsrPwd().equals(usrPwd)) {
				return true;
			}
		}
		return false;
	}

}

    好了,这样的话,我们的迭代器模式就完全运用上了,迭代器方法的一般步骤就是要定义一个 Iterator 接口,提供 next 和 hasNext 抽象方法。第二步去搞几个实现类去实现这个  Iterator 接口。第三步再定义一个 SubIterator  接口,提供一个具有 Iterator<T> 的返回值的抽象方法,然后在真正需要的类中去实现 SubIterator 接口,实例化实现类即可。

    关于迭代器模式的所以内容就这样了,相比之下,客户端的代码果然减少许多,但同时也增加了好几个类,至于怎么使用、怎么抉择还是得需要我们根据合适的场景来判断,不能因为设计模式比较高大上,就盲目去使用它,跳入过渡设计的坑。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值