読者です 読者をやめる 読者になる 読者になる

あと味

たくさん情報を食べて、たくさん発信すると、あとになって味わい深い。

MooseでIteratorパターン

Perl

最近、Perlでとあるプログラムを作成しているのですが、動くコードは書けるものの、納得できるコードが書けないというジレンマに陥っています。オブジェクト指向で作りたいとは思いつつ、手続きで書く→オブジェクト指向っぽく直す→これでいいの本当に?みたいな悪循環にハマります。要するに経験不足なので、設計ができないし、良い設計がどんなものかもわかんないんですよね。

ということで、一旦脇道にそれて、良い設計のお手本とも言うべきデザインパターンに入門してみることにしました。

教科書はいろんな人が教科書にしているid:hyukiさんのデザパタ本です。ずっと手元には置いていたものの、実装したことがなかったので、紹介されているパターンを前から順番に実装してみることにしました。

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

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

さらに、Mooseの使い方が詳しく載っているid:lestrratさんのモダンPerl入門も教科書にしました。本書にもIteratorパターンは載っているのですが、ちょっと高度で私には難しかったので、Mooseの使い方は牧さんのモダンPerl入門Iteratorの実装方法は結城さんのデザパタ本を参考にしています。

モダンPerl入門 (CodeZine BOOKS)

モダンPerl入門 (CodeZine BOOKS)

その他、id:chaichanPaPaさんの以下の記事も参考にさせていただきました。

コード

Aggrigate.pm
package Aggregate;
use Moose::Role;

requires 'iterator';

no Moose::Role;

1;
Iterator.pm
package Iterator;
use Moose::Role;

requires qw( hasNext next );

no Moose::Role;

1;
Book.pm
package Book;
use Moose;

has 'name' => ( is => 'rw' );

no Moose;

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

sub getName {
  my $self = shift;
  $self->name;
}

1;
BookShelf.pm
package BookShelf;
use Moose;
use BookShelfIterator;

with 'Aggregate';

has 'books' => ( is => 'rw' );
has 'last'  => ( is => 'rw' );

no Moose;

sub BUILDARGS {
  my $self = shift;
  return { books => [], last => 0 };
}

sub getBookAt {
  my ($self, $index) = @_;
  $self->books->[$index];
}

sub appendBook {
  my ($self, $book) = @_;
  $self->books->[$self->last] = $book;
  $self->{last}++;
}

sub getLength {
  my $self = shift;
  return $self->last;
}

sub iterator {
  my $self = shift;
  BookShelfIterator->new($self);
}

1;
BookShelfIterator.pm
package BookShelfIterator;
use Moose;

with 'Iterator';

has 'bookShelf' => ( is => 'rw' );
has 'index' => ( is => 'rw' );

no Moose;

sub BUILDARGS {
  my ($self, $bookShelf) = @_;
  return { bookShelf => $bookShelf, index => 0 };
}

sub hasNext {
  my $self = shift;
  if ($self->index < $self->bookShelf->getLength ) {
    return 1;
  }
  else {
    return ();
  }
}

sub next {
  my $self = shift;
  my $book = $self->bookShelf->getBookAt($self->index);
  $self->{index}++;
  return $book;
}

1;
main.pl
use strict;
use warnings;

use Perl6::Say;

use BookShelf;
use Book;

my $bookShelf = BookShelf->new;
$bookShelf->appendBook(Book->new('Around the World in 80 Days'));
$bookShelf->appendBook(Book->new('Bible'));
$bookShelf->appendBook(Book->new('Cinderella'));
$bookShelf->appendBook(Book->new('Daddy-Long-Legs'));
my $it = $bookShelf->iterator;
while ($it->hasNext) {
  my $book = $it->next;
  say $book->getName;
}

感想

ぶっちゃけ、これを写生しただけではよくわかりませんでした。なんで抽象メソッド化すると幸せなのか?とかしっくりとはわかりません。

型チェックとか、アクセス指定(privateとかpublic)は何も考えてません。

あと、Mooseでコンストラクタを書く時に結構ハマりました。BUILDARGSのとこです。newの引数をハッシュにすれば悩む必要はないみたいなんですが、引数に値を直接渡す時にはMooseじゃない方がむしろ簡単なのかもと思いました。たぶん慣れの問題だとは思いますけど。

こんな感じで結城さんのデザパタ本を進めていくと少しはオブジェクト指向の設計がわかってくるのかなー。。。それとも、第三者にコードを利用してもらうことを前提としない限り、オブジェクト指向の設計の勘所はわからないのかしら。

オブジェクト指向のプログラミングって、自分だけが利用するプログラムを書くことがほとんどなアマグラマーには、無用の長物のような気がしないではないですが、自分の書いたコードが納得できないんだから勉強しなきゃはじまらないですね。

追伸

ついでに、これを機にgithubのアカウントを取ってみたり。このサンプルコードを上げてあります。今後有効活用できるといいなと思っています。

でも、修正の履歴が残るのはなんだか恥ずかしいですね。。。