ziguzagu.org

Iteratorパターン

せっかくの休日なのになんにもやる気がおこらずだらだらする。。。なにがきっかけかもう忘れたけど、そんなだらだらな日は↓の本をPerlでだらだら書いてみることにする。

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

23パターンのうち半分くらいはちゃんと理解しているつもり。人にちゃんと説明できる、書ける、使える、そんな立派な大人にならないといけない。

Iteratorインターフェースから。

package Iterator;
use strict;
use warnings;

sub has_next { die; }
sub get_next { die; }

1;

Aggregaterインターフェース。

package Aggregate;
use strict;
use warnings;

sub iterator { die; }

1;

今回扱うオブジェクトのBookクラス。

package Book;
use strict;
use warnings;

sub new {
    my ($class, $name) = @_;
    my $self = {
        name => $name,
    };
    return bless $self, $class;
}

sub name {
    return shift->{name};
}

1;

Bookオブジェクトの集まり、Bookshelf。Aggregateを実装する。

package Bookshelf;
use strict;
use warnings;
use base qw/Aggregate/;
use BookshelfIterator;

sub new {
    my ($class, $maxsize) = @_;
    my $self = {
        maxsize => $maxsize || 5,
        books   => [],
    };
    return bless $self, $class;
}

### implements Aggregate::iterator
sub iterator {
    my $self = shift;

    return BookshelfIterator->new($self);
}

sub get_book_at {
    my ($self, $index) = @_;

    return $self->{books}->[$index];
}

sub append_book {
    my ($self, $book) = @_;

    if (scalar @{$self->{books}} < $self->{maxsize}) {
        push @{$self->{books}}, $book;
    }
}

sub get_length {
    return scalar @{shift->{books}};
}

1;

maxsizeの扱いは不親切。警告なし、追加しない。

この集まりBookshelfをスキャンしてIteratorとして扱えるようにする、BookshelfIterator。Iteratorを実装する。

package BookshelfIterator;
use strict;
use warnings;
use base qw/Iterator/;
use Bookshelf;

sub new {
    my ($class, $bookshelf) = @_;
    my $self = {
        bookshelf => $bookshelf,
        id        => 0,
    };

    return bless $self, $class;
}

### implements Iterator::has_next
sub has_next {
    my $self = shift;

    return $self->{id} < $self->{bookshelf}->get_length() ? 1 : 0;
}

### implements Iterator::get_next
sub get_next {
    my $self = shift;

    my $book = $self->{bookshelf}->get_book_at($self->{id});
    $self->{id}++;

    return $book;
}

1;

使ってみる。

#!/usr/bin/perl
use strict;
use warnings;
use Book;
use Bookshelf;

my $bookshelf = Bookshelf->new(4);
$bookshelf->append_book(Book->new("デザインパターン入門"));
$bookshelf->append_book(Book->new("Perl Best Practices"));
$bookshelf->append_book(Book->new("ウェブ進化論"));
$bookshelf->append_book(Book->new("その日のまえに"));

my $it = $bookshelf->iterator();
while ($it->has_next()) {
    my $book = $it->get_next();
    print $book->name() , "\n";
}

ようするに、

Iteratorパターンとは、何かがたくさん集まっているときに、それを順番に指し示していき、全体をスキャンしていく処理を行うためのものです。

ということ。もっとPerlらしい実装はあるだろうけど。

すこし、だらだらな1日を抜け出した気がする。。。