设计模式-迭代器(译)

192 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第4天,点击查看活动详情

迭代器模式是一种行为设计模式,迭代器模式提供一个标准的方式来迭代一组对象。迭代器模式被广泛应用在java集合框架中。迭代器接口提供遍历集合的方法

迭代器模式

根据GoF,迭代器的目的如下:

提供一种方式访问集合对象的成员并且不暴露底层表现

迭代器模式不仅用来遍历集合,我们也可以根据我们的实现来提供不同类型的迭代器

迭代器模式隐藏了具体的遍历集合实现,客户端程序仅仅使用了迭代器方法

迭代器模式示例

让我们用一个简单的例子来理解迭代器模式。假设我们有一个广播频道的集合,客户端程序想通过频道类型来一个一个遍历频道。举个例子,有些用户只对英语频道感兴趣且只想操作这个类型,他们不想去操作其他类型的频道

因此我们提供一个频道集合和一个客户端程序,让客户端程序写自己的逻辑来遍历这些频道,且决定是否操作它们。但是这种方案有很多的问题,比如客户程序需要提供一个遍历逻辑。我们不能保证客户程序的遍历程序是正确的。进一步来说,随着客户端程序的变多,也会变得越来越难以维护

这儿我们可以使用迭代器模式来提供一个基于频道类型的迭代。我们要保证客户端程序只能通过迭代器来遍历频道列表。

第一步提供一个集合定义和一个迭代器接口

ChannelTypeEnum.java

package com.journaldev.design.iterator;

public enum ChannelTypeEnum {

	ENGLISH, HINDI, FRENCH, ALL;
}

ChannelTypeEnum 是一个java枚举,来定义不同类型的频道

Channel.java

package com.journaldev.design.iterator;

public class Channel {

	private double frequency;
	private ChannelTypeEnum TYPE;
	
	public Channel(double freq, ChannelTypeEnum type){
		this.frequency=freq;
		this.TYPE=type;
	}

	public double getFrequency() {
		return frequency;
	}

	public ChannelTypeEnum getTYPE() {
		return TYPE;
	}
	
	@Override
	public String toString(){
		return "Frequency="+this.frequency+", Type="+this.TYPE;
	}
	
}

Channel 是一个简单的pojo类,有频率和类型两个属性

ChannelCollection.java

package com.journaldev.design.iterator;

public interface ChannelCollection {

	public void addChannel(Channel c);
	
	public void removeChannel(Channel c);
	
	public ChannelIterator iterator(ChannelTypeEnum type);
	
}

ChannelCollection接口定义了我们集合类的抽象协议。可以看到这儿有新增和删除频道方法,但是没有一个返回频道列表的方法。ChannelCollection有一个方法返回迭代器来进行遍历。ChannelIterator接口定义如下:

package com.journaldev.design.iterator;

public interface ChannelIterator {

	public boolean hasNext();
	
	public Channel next();
}

现在我们的基础接口和核心类都已经准备好了,让我们来来处理基于集合类和迭代器接口的实现类 ChannelCollectionImpl.java

package com.journaldev.design.iterator;

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

public class ChannelCollectionImpl implements ChannelCollection {

	private List<Channel> channelsList;

	public ChannelCollectionImpl() {
		channelsList = new ArrayList<>();
	}

	public void addChannel(Channel c) {
		this.channelsList.add(c);
	}

	public void removeChannel(Channel c) {
		this.channelsList.remove(c);
	}

	@Override
	public ChannelIterator iterator(ChannelTypeEnum type) {
		return new ChannelIteratorImpl(type, this.channelsList);
	}

	private class ChannelIteratorImpl implements ChannelIterator {

		private ChannelTypeEnum type;
		private List<Channel> channels;
		private int position;

		public ChannelIteratorImpl(ChannelTypeEnum ty,
				List<Channel> channelsList) {
			this.type = ty;
			this.channels = channelsList;
		}

		@Override
		public boolean hasNext() {
			while (position < channels.size()) {
				Channel c = channels.get(position);
				if (c.getTYPE().equals(type) || type.equals(ChannelTypeEnum.ALL)) {
					return true;
				} else
					position++;
			}
			return false;
		}

		@Override
		public Channel next() {
			Channel c = channels.get(position);
			position++;
			return c;
		}

	}
}

可以看到内部类实现了迭代器接口,因此这个实现不可以被用于其他的集合。集合类都遵循相同的方式,都含有一个迭代器接口的实现内部类

让我们写一个简单的迭代器模式的测试程序,来使用的迭代器遍历频道集合


package com.journaldev.design.iterator;

public class IteratorPatternTest {

	public static void main(String[] args) {
		ChannelCollection channels = populateChannels();
		ChannelIterator baseIterator = channels.iterator(ChannelTypeEnum.ALL);
		while (baseIterator.hasNext()) {
			Channel c = baseIterator.next();
			System.out.println(c.toString());
		}
		System.out.println("******");
		// Channel Type Iterator
		ChannelIterator englishIterator = channels.iterator(ChannelTypeEnum.ENGLISH);
		while (englishIterator.hasNext()) {
			Channel c = englishIterator.next();
			System.out.println(c.toString());
		}
	}

	private static ChannelCollection populateChannels() {
		ChannelCollection channels = new ChannelCollectionImpl();
		channels.addChannel(new Channel(98.5, ChannelTypeEnum.ENGLISH));
		channels.addChannel(new Channel(99.5, ChannelTypeEnum.HINDI));
		channels.addChannel(new Channel(100.5, ChannelTypeEnum.FRENCH));
		channels.addChannel(new Channel(101.5, ChannelTypeEnum.ENGLISH));
		channels.addChannel(new Channel(102.5, ChannelTypeEnum.HINDI));
		channels.addChannel(new Channel(103.5, ChannelTypeEnum.FRENCH));
		channels.addChannel(new Channel(104.5, ChannelTypeEnum.ENGLISH));
		channels.addChannel(new Channel(105.5, ChannelTypeEnum.HINDI));
		channels.addChannel(new Channel(106.5, ChannelTypeEnum.FRENCH));
		return channels;
	}

}

运行程序,输出如下


Frequency=98.5, Type=ENGLISH
Frequency=99.5, Type=HINDI
Frequency=100.5, Type=FRENCH
Frequency=101.5, Type=ENGLISH
Frequency=102.5, Type=HINDI
Frequency=103.5, Type=FRENCH
Frequency=104.5, Type=ENGLISH
Frequency=105.5, Type=HINDI
Frequency=106.5, Type=FRENCH
******
Frequency=98.5, Type=ENGLISH
Frequency=101.5, Type=ENGLISH
Frequency=104.5, Type=ENGLISH

重要点

  • 当你想为集合提供一个标准的遍历方法时,迭代器模式就很有用,它可以在客户程序中隐藏遍历的实现逻辑
  • 迭代的逻辑被嵌入在集合类本身里,可以更容易让客户程序更容易的进行遍历

JDK中迭代器模式

  • 我们知道集合框架中的迭代是一个最好的例子。但是你知道java.util.Scanner类也是迭代器的一种实现。可以查看这篇文章来学习Java Scanner Class.

这就是迭代器模式,希望他对大家有所有帮助且容易理解

原文链接