ウェブエンジニア珍道中

日々の技術的に関する経験を書いていきます、脱線もします。

typescriptでデザインパターンを書いてみる -Iterator-

増補改訂版Java言語で学ぶデザインパターン入門

増補改訂版Java言語で学ぶデザインパターン入門

以前この本を読んでデザインパターンを勉強していました。

が、Rubyエンジニアの僕では Java→Rubyの脳内変換ができずに挫折してました。(Interfaceとか色々足りないし!)

最近触りだしたTypescriptなら材料的には結構できそうだったので、Javaから書き換えてまとめていこうかなーと思います。

リポジトリはこちらにあります、更新していくので良かったら見ていって下さい。指摘は大歓迎でございます。

github.com

Iteratorパターン

「反復子」という意味で、要素の集まり(ArrayとかListとか)に対して順番に行う処理を提供するパターンです。自分は「forとかで配列回してるのをもっといい感じにパーツ分けして保守しやすいようにしようぜ」くらいに解釈してます。

デザインパターンというものを理解する第一歩として適しているらしいです、入門者向けのデザインパターンなのかなってとこです。

作るもの

  • 本(Book)がある
  • 本をしまえる本棚(BookShelf)がある
  • 本棚に対していい感じに収納している本を表示させる

ソース

では早速ソースを貼っていきます。

Aggregate

集合体を表すインターフェースです。Iteratorを組み込むものに関してはこいつを継承してあげればiterator()メソッドの実装が義務付けられるのでわかりやすいです。

import {Iterator} from "./iterator"

export interface Aggregate {
    iterator(): Iterator;
}

Iterator

Iteratorのインターフェースです。実際に反復の処理を書くにはこのインターフェースを継承することで組み込むべきメソッド等が定められるので保守がしやすくなります。(多分)

export interface Iterator {
    hasNext(): boolean;
    next(): Object;
}

Book

本です。特殊な処理はなく、名前を登録・表示するだけです。

export class Book {
    private name_: String;

    constructor(name: String) {
        this.name_ = name;
    }

    get name() {
        return this.name_;
    }
}

BookShelf

本棚です。本を複数格納し何番目か指定するとその本を返却したり、今何冊本があるか出力したりします。

iterator()メソッドでは次のBookShelfIteratorクラスのインスタンスを提供します。これを使うことで反復処理を行います。BookShelfIteratorとは対の存在です。

import { Aggregate } from './aggregate'
import { Book } from './book'
import { Iterator } from './iterator'
import { BookShelfIterator } from './bookShelfIterator'

export class BookShelf implements Aggregate {
    private books: Book[];
    private last: number = 0;

    public constructor(maxsize: number) {
        this.books = new Array(maxsize);
    }

    public getBookAt(index: number): Book {
        return this.books[index];
    }

    public appendBook(book: Book): void {
        this.books[this.last++] = book;
    }

    public get length(): number {
        return this.last;
    }

    public iterator(): Iterator {
        return new BookShelfIterator(this);
    }
}

BookShelfIterator

本棚の本をまとめて処理するのを手伝う人です。BookShelfクラスのiterator()メソッドによって生成されて、next()hasNext()によって繰り返し処理ができる状態を提供します。BookShelfクラスとは対の存在です。

import { Iterator } from './iterator'
import { BookShelf } from './bookShelf'

export class BookShelfIterator implements Iterator {
    private bookShelf: BookShelf;
    private index: number;

    constructor(bookShelf: BookShelf) {
        this.bookShelf = bookShelf;
        this.index = 0;
    }

    public hasNext(): boolean {
        if (this.index < this.bookShelf.length) {
            return true;
        } else {
            return false;
        }
    }

    public next(): Object {
        return this.bookShelf.getBookAt(this.index++);
    }
}

実行処理(main.ts)

実際に処理を行います。本棚を用意し本を収納して、iteratorを使って本棚の中の本をコンソール上に出力しています。

import { Iterator } from './iterator';
import { BookShelf } from './bookShelf';
import { Book } from './book';

var bookShelf: BookShelf = new BookShelf(4);
bookShelf.appendBook(new Book("こころ"));
bookShelf.appendBook(new Book("人間失格"));
bookShelf.appendBook(new Book("君の名は"));
bookShelf.appendBook(new Book("ぐりとぐら"));

console.log("以下本棚の本");
var it: Iterator = bookShelf.iterator();

while(it.hasNext()) {
    console.log(<Book>it.next());
}

実行結果

tsc # コンパイル
node main.js
こころ
人間失格
君の名は
ぐりとぐら

メリット

hasNext()next()の出力結果さえ変えなければ実行する側は変えなくても大丈夫で保守しやすいぜ」ってところです。

今回はこの本棚クラスを使う人は一人しかいませんが、このmain.tsのような処理が100個あると大分メリットが見えてくるかなと思います。

この繰り返し処理に改修が入った時に全てバラバラにfor文を書いていたら100箇所直さないといけませんが、iteratorパターンを使って書いていると一箇所書き換えるだけで済みます。便利。

まだまだ十数個パターンはあるようなのでコツコツ理解していきたいなーという感じです。では。