帳簿で学ぶデザインパターン - 2
Iterator
- 1 => プロローグ
- 2 => Iterator現在地
What are we making?
プロローグの段階から帳簿帳簿と言っているが、具体的には『購入した本を記録し、本に注ぎ込んだお金を計算して自らを戒める』システムを作っていく。
本の帳簿ということは、英語で言えば「Book of Book」になるのだろうか…(小声)
今回はシリーズ第一弾ということで、登録した本の情報を読み上げるだけのプログラムを作成する。
なお、記事もできる限り更新しようとは思うが、最新のコードは下記のリポジトリを参照してください。
https://github.com/tetracalibers/Learning-Design-Patterns-by-the-Book/tree/main/keepAccount/iterator
全体の構造
ディレクトリ構造は以下の通り。
current/
`-- keepAccount/
`-- Iterator/
|-- Book.java
|-- BookShelf.java
|-- BookShelfIterator.java
`-- Main.java
Bookクラス(本の情報を保持)
Book.java
package keepAccount.iterator;
import java.lang.StringBuffer;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Calendar;
public class Book {
private String title;
private String purchaseDate;
private int price;
private int discount;
private String paymentMethod;
private String shop;
public Book(String title) {
this.title = title;
this.purchaseDate = getToday();
this.price = 0;
this.discount = 0;
this.paymentMethod = "未登録";
this.shop = "未登録";
}
public String getToday() {
SimpleDateFormat dateType = new SimpleDateFormat("yyyy/mm/dd");
Calendar calendar = Calendar.getInstance();
Date today = calendar.getTime();
String todayStr = dateType.format(today);
return todayStr;
}
public void setPurchaseDate(String purchaseDate) {
this.purchaseDate = purchaseDate;
}
public void setPrice(int price) {
this.price = price;
}
public void setDiscount(int discount) {
this.discount = discount;
}
public void setPaymentMethod(String paymentMethod) {
this.paymentMethod = paymentMethod;
}
public void setShop(String shop) {
this.shop = shop;
}
public String getShop() {
return this.shop;
}
public String getPaymentMethod() {
return this.paymentMethod;
}
public int getAmountPaid() {
return this.price - this.discount;
}
@Override
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append(purchaseDate).append("に");
sb.append(shop).append("で購入");
sb.append("「").append(title).append("」");
sb.append(price).append("円から").append(discount).append("円値引きで");
sb.append(price - discount).append("円を");
sb.append(paymentMethod).append("で支払い");
return sb.toString();
}
}
BookShelfクラス(本とスキャナを保持)
BookShelf.java
package keepAccount.iterator;
import java.util.Iterator;
import java.util.ArrayList;
import java.util.List;
public class BookShelf implements Iterable<Book> {
private List<Book> bookShelf;
public BookShelf(int initialCount) {
this.bookShelf = new ArrayList<>(initialCount);
}
public void addBook(Book book) {
bookShelf.add(book);
}
public Book getBookAt(int index) {
return bookShelf.get(index);
}
public int getCount() {
return bookShelf.size();
}
@Override
public Iterator<Book> iterator() {
return new BookShelfIterator(this);
}
}
BookShelfIteratorクラス(本棚を走査するスキャナ)
BookShelfIterator.java
package keepAccount.iterator;
import java.util.Iterator;
import java.util.NoSuchElementException;
public class BookShelfIterator implements Iterator<Book> {
private BookShelf books;
private int index;
public BookShelfIterator(BookShelf books) {
this.books = books;
this.index = 0;
}
@Override
public boolean hasNext() {
if (index < books.getCount()) {
return true;
} else {
return false;
}
}
@Override
public Book next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
Book book = books.getBookAt(index++);
return book;
}
}
実行
Main.java
import keepAccount.iterator.*;
import java.util.Iterator;
public class Main {
public static void main(String[] args) {
BookShelf bookShelf = new BookShelf(5);
Book book1 = new Book("スラスラわかるJava第2版");
book1.setPurchaseDate("2021/11/20");
book1.setPrice(600);
book1.setDiscount(52);
book1.setPaymentMethod("クレジットカード");
book1.setShop("メルカリ");
bookShelf.addBook(book1);
Book book2 = new Book("Rubyの冒険 遊遊編");
book2.setPurchaseDate("2021/11/20");
book2.setPrice(400);
book2.setPaymentMethod("メルペイ残高");
book2.setShop("メルカリ");
bookShelf.addBook(book2);
Book book3 = new Book("Rubyの冒険 旅立ち編");
book3.setPurchaseDate("2021/11/20");
book3.setPrice(400);
book3.setPaymentMethod("メルペイ残高");
book3.setShop("メルカリ");
bookShelf.addBook(book3);
Iterator<Book> it = bookShelf.iterator();
while (it.hasNext()) {
Book book = it.next();
System.out.println(book);
}
}
}
実行すると次のようにターミナルに出力される。
cd currentまでのパス && javac -d keepAccount/iterator/bin keepAccount/iterator/Main.java && java -classpath keepAccount/iterator/bin Main
2021/11/20にメルカリで購入「スラスラわかるJava第2版」600円から52円値引きで548円をクレジットカードで支払い
2021/11/20にメルカリで購入「Rubyの冒険 遊遊編」400円から0円値引きで400円をメルペイ残高で支払い
2021/11/20にメルカリで購入「Rubyの冒険 旅立ち編」400円から0円値引きで400円をメルペイ残高で支払い
今回の登場人物
- Iterator(反復子抽象
- 担当者:Iteratorインターフェース
- 要素を走査するスキャナを抽象化したもの。
- ConcreateIterator(具体的な反復子具体
- 担当者:BookShelfIteratorクラス
- 要素を走査するスキャナ。
- Aggregate(集合体抽象
- 担当者:Iterableインターフェース
- 多数の要素を持ち、それらを走査するオブジェクトを生成する役を抽象化したもの。
- ConcreateAggregate(具体的な集合体具体
- 担当者:BookShelfクラス
- 多数の要素を持ち、それらを走査するオブジェクトを生成する役。
あとがき
オブジェクト指向におけるオブジェクトとは、「何かを保持し、その何かに対する処理もセットにしたもの」なのだと再認識させられる話題だった。
Bookクラスは本の情報を保持し、その本の情報を提供したり、情報を設定したりする役割も担う。
同様に、BookShelfクラスには本を保持させ、その本一つ一つに対する処理を行う役割も担わせたくなる。
しかし、「各要素一つ一つに対する処理」は、その要素が何であろうと大体共通の手順になるだろうから、その役割だけ再利用可能な部品として切り出しておく。
…イテレータはそんな風にして生まれたものなんじゃないだろうか。
そういえば、他のオブジェクト指向言語では、イテレータとジェネレータはよくセットで語られる。
ジェネレータはイテレータを生成するもの、つまり今回のAggregateの別名なのだろうか?(無知)