Java

【Java】IteratorとPrototypeでリストを作成する

こんにちは、ともです。

本日は、IteratorとPrototypeのデザインパターンを利用し、リストを作成する方法について考えていきます。この2つのデザインパターンを利用することで、同じ情報を配列に簡単に持たせることができます。

  • Bookクラス・・・本の情報をもつクラス
  • BookListクラス・・・本の情報を格納するクラス

という2つのクラスを用意します。BookListにBookを格納していきます。同じ情報を持つBookをBookListに格納するとき、どのようにコードを書きますか。

Book book1 = new Book("タイトル");
Book book2 = new Book("タイトル");
Book book3 = new Book("タイトル");

BookList bookList = new BookList();
bookList.addBook(book1);
bookList.addBook(book2);
bookList.addBook(book3);

という風にBookのインスタンスを何度もnewし、格納することを考えたかも知れません。これはbookの数が増えると大変ですね。

最近、GoFのデザインパターンを勉強中の私は、こういった状況に対処する方法を思いつきました。もっといい方法があるんだと思いますが。

今回はGoFのデザインパターンである、IteratorとPrototypeを利用し、BookとBookListを拡張します。

GoFのデザインパターンを勉強するにはTECHSCOREが便利ですので、合わせて読んでみて下さい。

クラス図

完成のクラス図を作成してみました。

Prototypeについては、Bookクラスのcloneメソッドで実装します。

BookListにIteratorを実装した、MyBookListを作成しました。Iteratorとしての機能はBookListIteratorが持っています。

PrototypeでBookを複製できるようにする

Bookを複製できるようにします。これにはProtypeというデザインパターンが有効です。Prototypeは、雛形を元に複製するという考えに近いです。

java.lang.Cloneableインタフェースを実装したクラスはcloneメソッドを利用できるようになります。

package Prototype;

public class Book implements Cloneable {
	private String title;

	public String getTitle() {
		return title;
	}

	public void setTitle(String title) {
		this.title = title;
	}
	
	public Book(String title) {
		setTitle(title);
	}

	public Book clone () {
		Book book = null;
		try {
			 book = (Book) super.clone();
		} catch (CloneNotSupportedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return book;
	}
}

これにより次のように複製が可能です。

package Prototype;

public class Main {

	public static void main(String[] args) {
		Book book = new Book("吾輩は猫である");
		
		MyBookList myBookList = new MyBookList();
		for(int i = 0; i < 10; ++i) {
			// Prototypeパターン
			myBookList.addBook(paper.clone());
		}
}

我輩は猫であるというBookを一冊作成し、その情報を元にインスタンスを生成します。これで何度もnewする必要は無くなります。

IteratorでBookListに繰り返し処理を実装する

MyBookListなんて作成せずに、ArrayListに詰め込めば実装できるんですがそれだとデザインパターンの復習にならないので、今回はMyBookListというIteratorパターンで実装されたリストを作成してみます。

BookListの作成

package Prototype;

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

public class BookList {
	public  int last;
	private List<Book> bookList = new ArrayList<Book>();
	
	public BookList() {}
	
	public int getLast() {
		return this.bookList.size() - 1;
	}
	
	public Book getPaperAt(int index) {
		return this.bookList.get(index);
	}
	
	public boolean addBook(Book book) {
		return this.bookList.add(book);
	}
}

Bookの情報をListに格納できるBookListを作成しました。次にBookListにIteratorの機能を追加したMyBookListを作成します。

そのために、Iterator/Aggregateインタフェースを作成しましょう。

Iterator / Aggregateインタフェースの作成

Iterator(反復)インタフェースは、次のindexにデータが格納されているかを判定する機能(hasNext)と次のindexのBookを取得する機能(next)の実装を要求します。

package Prototype;

public interface Iterator {
	public boolean hasNext();
	public Object next();
}

Aggregate(集計・凝集)インタフェースはIteratorの機能をもつクラスを返してくれる機能を実装することを要求します。

このIteratorインタフェースにより、MyBookListIteratorというMyBookListの繰り返し処理機能を持つクラスを作成します。

package Prototype;

public interface Aggregate {
	Iterator iterator();
}

このAggregateインタフェースにより、MyBookListが繰り返し処理機能をもつようにします。

BookListIteratorの作成

package Prototype;

public class BookListIterator implements Iterator{
	public int index = 0;
	public MyBookList paperList;
	
	public BookListIterator(MyBookList paperList) {
		this.paperList = paperList;
	}
	
	@Override
	public boolean hasNext() {
		
		boolean result = true;
		
		if(index >= paperList.getLast()) {
			result = false;
		}
		
		++index;
		
		return result;
	}

	@Override
	public Object next() {
		return paperList.getPaperAt(index);
	}
}

BookListIteratorではコンストラクタでMyBookListを受け取ります。

hasNextメソッドで、次のインデックスに値があるかを判定します。

nextメソッドにより、次のインデックスの値を返します。

Iteratorはリストを探索する機能を提供してくれます。

MyBookListの作成

package Prototype;

public class MyBookList extends BookList implements Aggregate {

	@Override
	public Iterator iterator() {
		return new BookListIterator(this);
	}
	
}

MyBookListはBookListを継承し、Aggregateを実装します。

つまり、

  • BookListのリストとしての機能
  • イテレータの機能を

の2つを持たせています。

まとめ

Iteratorの機能とPrototypeの機能を合わせてリストを作成してみました。PrototypeとIteratorの良い勉強になりました。

初めはインターフェースについて難しいイメージがありましたが、色々なデザインパターンを自分で書いてみることで慣れることができますね。

先人の例に習って勉強を進めて行きたいと思います。