Iterator <- 帳簿で学ぶデザインパターン <- TetraCalibers
TetraCalibers :
    -   for : science student & programmer
帳簿で学ぶデザインパターン - 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の別名なのだろうか?(無知)