JavaでのIteratorデザインパターン

イテレータデザインパターンは、行動パターンの一つです。イテレータパターンは、オブジェクトのグループを横断するための標準的な方法を提供するために使用されます。イテレータパターンは広くJava Collection Frameworkで使用されています。イテレータインターフェースは、コレクションを横断するためのメソッドを提供します。

イテレータデザインパターン

GoFによれば、イテレータデザインパターンの目的は:

集約オブジェクトの要素にアクセスする方法を提供し、その基本的な表現を公開せずにいます。

イテレータパターンは、コレクションを横断するだけでなく、要件に基づいて異なる種類のイテレータを提供することができます。イテレータデザインパターンは、コレクションを横断する実際の実装を隠し、クライアントプログラムは単にイテレータのメソッドを使用します。

イテレータパターンの例

単純な例でイテレーターパターンを理解しましょう。ラジオチャンネルのリストがあり、クライアントプログラムはそれらを1つずつまたはチャンネルの種類に基づいてトラバースしたいとします。たとえば、一部のクライアントプログラムは英語のチャンネルにのみ興味があり、それらのみを処理したいとします。他の種類のチャンネルを処理したくないのです。したがって、チャンネルのコレクションをクライアントに提供し、それらをトラバースして処理するためのロジックを書かせることができます。ただし、この解決策には多くの問題があります。たとえば、クライアントはトラバースのためのロジックを考えなければなりませんが、そのロジックが正しいかどうかを確認することはできません。さらに、クライアントの数が増えると、メンテナンスが非常に困難になります。ここで、イテレーターパターンを使用して、チャンネルの種類に基づいた反復を提供できます。クライアントプログラムがチャンネルのリストにアクセスできるのはイテレーターを通じてのみであることを確認する必要があります。実装の最初の部分は、コレクションとイテレータのインターフェースの契約を定義することです。 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インターフェースは、次のメソッドを定義します;ChannelIterator.java

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;
		}

	}
}

内部クラスのイテレータインターフェースの実装に注目してください。これにより、実装は他のコレクションで使用できなくなります。コレクションクラスも同じアプローチが取られ、すべてのコレクションクラスにはIteratorインターフェースの内部クラス実装があります。コレクションとイテレータを使用して、チャンネルのコレクションをトラバースするための単純なイテレータパターンテストプログラムを書いてみましょう。IteratorPatternTest.java

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("******");
		// チャンネルタイプのイテレータ
		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のIteratorデザインパターン

私たちは皆、コレクションフレームワークのIteratorが反復子パターンの実装の最良の例であることを知っていますが、java.util.ScannerクラスもIteratorインターフェースを実装していることを知っていますか。この投稿を読んでJava Scannerクラスについて学んでください。反復子デザインパターンに関する情報はこれで以上です。役立つ情報であり、理解しやすいことを願っています。

Source:
https://www.digitalocean.com/community/tutorials/iterator-design-pattern-java