Spiffyでテンプレートメソッドを書いてみた

Kwikiで使われているSpiffyが気になったので テンプレートメソッドを書いてみた。

-Baseで継承すると、$self=shiftとパッケージの最後に1;をつけなくてもよくなるので、コード量が減るらしい

AbstractDisplay.pm

package AbstractDisplay;

use Spiffy -Base;

stub 'open';
stub 'print';
stub 'close';

sub display {
        $self->open;
        for my $i (0..4) {
                $self->print;
        }
        $self->close;
}

CharDisplay.pm

package CharDisplay;
use AbstractDisplay -Base;

sub new {
   my $char = shift;
   bless {ch => $char}, $self;
}

sub open {
   print "<<";
}

sub print {
   print "$self->{ch}";
}

sub close {
   print ">>\n";
}

StringDisplay.pm

package StringDisplay;
use AbstractDisplay -Base;

sub new {
   my $str = shift;
   my $w = length($str);
   bless {
       string => $str,
       width => $w
       }, $self;
}

sub open {
   $self->_printline;
}

sub print {
   print "|$self->{string}|\n";
}

sub close {
   $self->_printline;
}

sub _printline {
   print "+";
   for my $i (0..$self->{width}-1) {
       print "-";
   }
   print "+\n";
}

main.pl

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
#!/usr/bin/perl
use strict;

use CharDisplay;
use StringDisplay;

my $d1 = CharDisplay->new('H');
my $d2 = StringDisplay->new("Hello, World.");
my $d3 = StringDisplay->new("るるるるる");

$d1->display;
$d2->display;
$d3->display;

で実行してみる。

<<HHHHH>>
+-------------+
|Hello, World.|
|Hello, World.|
|Hello, World.|
|Hello, World.|
|Hello, World.|
+-------------+
+---------------+
|るるるるる|
|るるるるる|
|るるるるる|
|るるるるる|
|るるるるる|
+---------------+

抽象クラスのstubは実装をサブクラスに任すときに使う。サブクラスで実装されてないとdieする。

試しに、CharDisplayのprintメソッドがないと

Method print in package AbstractDisplay must be subclassed at \
/usr/lib/perl5/site_perl/5.8.8/Spiffy.pm line 279
        Spiffy::__ANON__('CharDisplay=HASH(0x9a49c28)') \
        called at AbstractDisplay.pm line 12
        AbstractDisplay::display('CharDisplay=HASH(0x9a49c28)') \
        called at ./main.pl line 11

とエラー吐いてとまる。

コードの見通しは良くなる感じ(もうちょっと複雑なことやってみないと分からんけど。)

Prototype ( デザパタPerl )

今回はperlのモジュール使って書いたヨ、というわけではなくインスタンスのコピーってどうやってつくんの?っていう話題だ。

naoyaのはてなダイアリー - prototype.js でデザインパターン - Prototype

登録されてるインスタンスを連想配列から取り出して返却するのですが、そのときにそのまま取り出して返却するんではなく、インスタンスに実装されてる createClone() を呼び出して返却すると。createClone() の中ではインスタンスのコピーが行われます。

で、PerlサンプルのProduct.pmみてみると

sub clone {
    my $self = shift;
    my $class = ref($self);
    bless {%{$self}}, $class;
}

sub createclone {
    my $self = shift;
    return $self->clone;
}

{%{$self}}がさっぱしわからん。なんでこれでインスタンス(ハッシュのリファレンス)のコピーが出来んの?

ってことを追求だ。

まず、インスタンスが異なるのかどうか、リファレンスをプリントして確認するためにmain.plをいじくる。

use strict;

use Manager;
use UnderlinePen;
use MessageBox;

my $manager = Manager->new;
my $upen = UnderlinePen->new('~');
$manager->register("strong message", $upen);

my $p11 = $manager->create("strong message");
my $p12 = $manager->create("strong message");

print $p11,"\n";
print $p12,"\n";

で実行。

$ perl main.pl 
UnderlinePen=HASH(0x8dc6604)
UnderlinePen=HASH(0x8dc78dc)

おー、かわっとる。うーん、、、、、

素直に考えると

  • $selfが%{$self}でリファレンスからハッシュへ
  • それをさらにbless {%{$self}},$classで無名ハッシュでブレスして戻す
  • だから、コピーされてる

んだろうなぁと思うんだけど。

試しにProduct.pmのcloneメソッドをこんな感じで変えてみる。

sub clone {
       my $self = shift;
       my $class = ref($self);
       bless \%{$self}, $class;
}

でさっきとおなじmain.plを実行

$ perl main.pl 
UnderlinePen=HASH(0x8491eb0)
UnderlinePen=HASH(0x8491eb0)

同じリファレンスだ。

ということはreturnされるときにコピーされるのかなぁ。うーん結局わからん。無名ハッシュ生成のメカニズムあたりをちゃんとチェックすれば解決するのか?

こういうメモリまわりを追っかける場合ってperl -dすればいいのかなぁ。

060412 追記

オブジェクト指向Perlマスターコースの3章に書いてあった

ProductName オブジェクト指向Perlマスターコース―オブジェクト指向の概念とPerlによる実装方法
ダミアン コンウェイ
ピアソンエデュケーション / ?円 ( 2001-02 )


refを使ってオブジェクトの型を調べつつ、新しく生成したオブジェクトをハッシュ間の単純代入をするといいらしい。

%{objref2 = ref($objref1)->new()} = %{$objref1}

だから、こんな感じでオブジェクトはコピーできるそうなんだ。

このエントリは結城浩さんの本と、それに載っているサンプルをPerlで書いた例をみながら考えたことをメモってます。あとは、はてなの伊藤さんのjavascriptでの例も参考にしてます。 間違いの指摘とか、もっといいやり方教授してくれたらとても嬉しいゾ。

ProductName 増補改訂版Java言語で学ぶデザインパターン入門
結城 浩
ソフトバンククリエイティブ / 3990円 ( 2004-06-19 )


Factory Method ( デザパタPerl )

Factory Methodはちゃんと理解するのにちょっと時間がかかった。

  • インスタンスの生成がTemplate Methodで抽象化されている
  • つまりインスタンス化をサブクラスに任せる

というところがポイントだが、ここの部分は抽象メソッドでなくても、デフォルトのメソッドを用意してもいいし、エラー吐くようにしてもいいらしい。ココなど詳しいが、

naoyaのはてなダイアリー - prototype.js でデザインパターン - Factory Method

クライアントで利用する主人公であるところのインスタンスの生成ロジックを Factory の中に抽象化してやって、具体的な new によるインスタンス生成の束縛からクラスを解放してやりましょう、ということになります。

つまり、コンストラクトするメソッドを適当な方法で実際のクラスと切り離すという理解でいいのかな。

CPAN探してみたらClass::Factory というモジュールを見つけたので、これを使ってデザパタ本のサンプルを書いてみた。

まず、FactoryクラスはClass::Factoryを使うので特に書く必要はない(モジュールのインストールだけ)。で、IDCardFactoryクラスはこんな風に書くだけでラクチン。

package IDCardFactory;
use strict;
use base qw(Class::Factory);

__PACKAGE__->register_factory_type( IDCard => 'IDCard' );

1;

Productクラスもサンプルの通り、何はともあれuse出来るもの

package Product;
use strict;

sub use { die; }

1;

で、実際のプロダクトの生成メソッドはinitなのでここをちょっと書く(といってもサンプルのnewとほとんど変わらんが)

package IDCard;
use strict;
use base qw(Product);

sub init {
    my ( $self, $owner) = @_;
    $self->{owner} = $owner;
    print "$self->{owner}のカードを作ります。\n";
    return $self;
}

sub use {
    my $self = shift;
    print "$self->{owner}のカードを使います。\n";
}

sub getowner {
    my $self = shift;
    return $self->{owner};
}

1;

さてメイン

#!/usr/bin/perl
BEGIN { unshift(@INC, "./"); }

use strict;
use IDCardFactory;

my $card1 = IDCardFactory->new('IDCard','kzfm');
my $card2 = IDCardFactory->new('IDCard','愛犬はな');
my $card3 = IDCardFactory->new('IDCard','U隊長');
my $card4 = IDCardFactory->new('IDCard','新入りゆき');

$card1->use;
$card2->use;
$card3->use;
$card4->use;

で、実行結果

$./main.pl 
kzfmのカードを作ります。
愛犬はなのカードを作ります。
U隊長のカードを作ります。
新入りゆきのカードを作ります。
kzfmのカードを使います。
愛犬はなのカードを使います。
U隊長のカードを使います。
新入りゆきのカードを使います。

これだと、ファクトリーのインスタンスが作られないのがサンプルと違うとこかな。あとはClass::Factoryのメソッドregister_factory_typeで幾つかのプロダクトを登録できるので、IDCardFactoryではなくてCardFactoryにして、VISA,Master,Nojima,Kojima,Yamadaカードみたいに複数のカードを登録しとくのが正しい使い方なのかな。

このエントリは結城浩さんの本と、それに載っているサンプルをPerlで書いた例をみながら考えたことをメモってます。あとは、はてなの伊藤さんのjavascriptでの例も参考にしてます。 間違いの指摘とか、もっといいやり方教授してくれたらとても嬉しいゾ。

ProductName 増補改訂版Java言語で学ぶデザインパターン入門
結城 浩
ソフトバンククリエイティブ / 3990円 ( 2004-06-19 )


Template Method ( デザパタPerl )

naoyaのはてなダイアリー - prototype.js でデザインパターン - Template Method

Iterator と Adapter では interface に相当するものは省略してきましたが、今回は抽象クラスをちゃんと用意します。JavaScript での interface みたいなものは Perl とかと一緒で実質意味を持ちませんが、抽象クラスなら実装がスーパークラスにちょこっとあるので意味があります。

Perlのコードだと、これのことか。

sub display {
    my $self = shift;
    $self->open;
    for my $i (0..4) {
        $self->print;
    }
    $self->close;
}

なるほど。そうすると抽象クラスでopen,print,closeメソッドでdieしておくことに意味があるのかも。でも、javaにはあるfinal修飾子がperlにはないから、オーバーライドされてしまうという危険性は残るよなぁ。

このエントリは結城浩さんの本と、それに載っているサンプルをPerlで書いた例をみながら考えたことをメモってます。あとは、はてなの伊藤さんのjavascriptでの例も参考にしてます。 間違いの指摘とか、もっといいやり方教授してくれたらとても嬉しいゾ。

ProductName 増補改訂版Java言語で学ぶデザインパターン入門
結城 浩
ソフトバンククリエイティブ / 3990円 ( 2004-06-19 )


Singleton ( デザパタPerl )

Singletonは結城さんの本の言葉を借りれば

  • 指定したクラスのインスタンスが絶対に一個しか存在しないことを保証したい
  • インスタンスが一個しかないことをプログラムで表現したい

ということになるわけですが、

perlではコンストラクタをprivateにしてしまうということが出来ないので、ココみたいにはいかないのは、おまえののログ に書いてある通りなのは理解できる。

でも、perlサンプルとかココみたいに、

package Singleton;
use strict;
use vars qw($singleton);

sub new {
    $singleton and return $singleton;
    my $class = shift;
    my $self = {};
    $singleton = $self;
    print "インスタンスを生成しました。\n";
    bless $self, $class;
}

1;

こんな感じで書くと駄目な理由がイマイチわからん。

ProductName Web+DB press (特別総集編)

技術評論社 / ?円 ( 2005-03 )


でも同じような書きかたしてたぞ。

このエントリは結城浩さんの本と、それに載っているサンプルをPerlで書いた例をみながら考えたことをメモってます。あとは、はてなの伊藤さんのjavascriptでの例も参考にしてます。 間違いの指摘とか、もっといいやり方教授してくれたらとても嬉しいゾ。

ProductName 増補改訂版Java言語で学ぶデザインパターン入門
結城 浩
ソフトバンククリエイティブ / 3990円 ( 2004-06-19 )


デザインパターンとJAVA

perlでモジュール作るときに、グダグダになってくることが多いので、いっちょデザインパターンの勉強を!

というわけで、購入したオライリーの本は言語がjavaだけど面白い。

ProductName Head Firstデザインパターン ―頭とからだで覚えるデザインパターンの基本
Eric Freeman
オライリージャパン / 4830円 ( 2005-12-02 )


買うときは4.6Kは費用対効果が、、、なんて思っていたが、コード自体は感覚的に理解できるのでなかなかためになる。

とはいえ実際にperlではどう書くんじゃとなったときに困るので、

ProductName Web+DB press (特別総集編)

技術評論社 / ?円 ( 2005-03 )


のデザインパターン特集を見ながら一緒に勉強した。

が、ちゃんとjavaを理解していないとオライリーの本がきちんと理解できんので、

近所の本屋で結城浩の本を買って、年始に読破。

ProductName 改訂第2版 Java言語プログラミングレッスン (上)
結城 浩
ソフトバンククリエイティブ / 2520円 ( 2005-10-26 )


ProductName 改訂第2版 Java言語プログラミングレッスン (下)
結城 浩
ソフトバンククリエイティブ / 2520円 ( 2005-10-26 )


上下巻ともに、すらすら読めてすごくわかりやすかった。特にインターフェースとかスレッドの部分はperlにはなかったような部分なのできちっと理解するように何度か読み返した(スレッドは5.8ではわからんけど)。上巻は基本的な部分でもともとある程度は理解していたのでほとんど斜め読みだったが、下巻はperlでoopを少しは理解していたという面もあるが、流れがスムーズでさらさら読み進めた。そして言語体系としてのjavaもちょっとは理解した(気がする)。

特に予約語とか慣習とかは知らないとオライリーの本はきつい部分があるので、これからオライリーのデザインパターン本は2週目読みますゾ。

Adaptor ( デザパタPerl )

結城さんの本には、委譲と継承の二通りの方法が記載されていたが、その二つの違い(メリット、デメリット)がイマイチ理解できなかったので、ググって見ると、YukiWikiに。

委譲 - Delegation

継承はそのインスタンスのなかの変数を使用するのに対し、委譲では委譲先のインスタンスの中の変数を使用する。(この説明は単に動作の違いを言っているだけかな。) 継承はインスタンスの振る舞い方を規定し、委譲はインスタンスの実装となる。 インスタンスの振る舞い方が変わると、そのインスタンスを利用するほうにも影響が出る。 インスタンスの実装は、使われ方で変わるが、利用するほうには影響が出にくい。

なんとなくわかったような、わからないような。

継承はそのインスタンスのなかの変数を使用するのに対し、委譲では委譲先のインスタンスの中の変数を使用する

というあたりをよく考えてみるといいのだろう。

perlで確認してみると、 サンプルは委譲を使っているので、僕はちょっと継承を用いたperlサンプルを書いてみた。

main.plとBanner.pmはサンプルのものをそのまま使用。interfaceにあたるprinter.pmは使わなかった。

package PrintBanner;
use strict;
use base qw(Banner);

sub new {
    my $class = shift;
    my $string = shift;
    my $self = $class->SUPER::new($string);
    bless $self, $class;
}

sub printweak {
    my $self = shift;
    $self->show_with_paren;
}

sub printstrong {
    my $self = shift;
    $self->show_with_aster;
}

1;

結局AdapteeとTargetの違いの大きさで使い分ければいいのかな?このサンプルだとメソッド名しか変わってないから、委譲を使ったほうがすっきりするのだろうか?

このエントリは結城浩さんの本と、それに載っているサンプルをPerlで書いた例をみながら考えたことをメモってます。あとは、はてなの伊藤さんのjavascriptでの例も参考にしてます。 間違いの指摘とか、もっといいやり方教授してくれたらとても嬉しいゾ。

ProductName 増補改訂版Java言語で学ぶデザインパターン入門
結城 浩
ソフトバンククリエイティブ / 3990円 ( 2004-06-19 )


Iterator::Utilをつかってみた ( デザパタPerl )

イテレータのサンプルを実際にIterator::Utilモジュール使って書いてみたゾ。

今回、用意したのはBook,BookShelf,mainだ。インターフェースにあたる部分は省略ってことで。

Bookはサンプルのコードをそのまま使用してます。 BookShelfはbooksメソッドで配列返すようにしといた。

package BookShelf;
use strict;

sub new {
    my $class = shift;
    my $self = {};
    $self->{maxsize} = shift;
    $self->{books} = [];
    $self->{last} = 0;
    bless $self, $class;
}

sub appendbook {
    my $self = shift;
    my $book = shift;
    $self->{books}->[$self->{last}] = $book;
    $self->{last}++;
}

sub books {
    my $self = shift;
    $self->{books};
}

1;

mainでIterator::Utilを呼ぶ。

!/usr/bin/perl

use strict; use BookShelf; use Book; use Iterator::Util;

my $bookshelf = BookShelf->new(5); $bookshelf->appendbook(Book->new('Java言語で学ぶデザインパターン入門')); $bookshelf->appendbook(Book->new('憂鬱なプログラマのためのオブジェクト指向開発講座')); $bookshelf->appendbook(Book->new('かんたんUML')); $bookshelf->appendbook(Book->new('プログラミングPerl')); $bookshelf->appendbook(Book->new('オブジェクト指向Perlマスターコース'));

my $it = iarray ($bookshelf->books);

while (my $book = $it->value) { print $book->getname."n"; }

実行結果はこんな感じ。最後のIterator is exhaustedがちょっと気持ち悪い。

$ perl main.pl 
Java言語で学ぶデザインパターン入門
憂鬱なプログラマのためのオブジェクト指向開発講座
かんたんUML
プログラミングPerl
オブジェクト指向Perlマスターコース
Iterator is exhausted at main.pl line 18

増補改訂版Java言語で学ぶデザインパターン入門あなたの考えを広げるためのヒント(ここがおもろい)には、

実装とは切り離して数え上げできることが重要

とか書いてあるので、まぁ、これでいいのかな。

このエントリは結城浩さんの本と、それに載っているサンプルをPerlで書いた例をみながら考えたことをメモってます。あとは、はてなの伊藤さんのjavascriptでの例も参考にしてます。 間違いの指摘とか、もっといいやり方教授してくれたらとても嬉しいゾ。

ProductName 増補改訂版Java言語で学ぶデザインパターン入門
結城 浩
ソフトバンククリエイティブ / 3990円 ( 2004-06-19 )


Iterator ( デザパタPerl )

Perlの例だとインターフェースのクラスをdieしているので、最初これはどういう意味があるのかわかんなかったが、

naoyaのはてなダイアリー - prototype.js でデザインパターン - Iterator

デザパタ本のコードでは Java の interface をまず用意して、となるのですが例によって JavaScript は動的な言語かつ型がないのでサブクラスに実装を強制させるメカニズムがない。ということで、ここでは interface に相当するものは用意せずに Concrete クラスだけで実装します。

と書いてあったのを見てサブクラスで実装されてない場合によばれた場合のためのちょっとした仕組みなのだとわかった(あがきにちかいような気もするが)。で、試しにjavaで実際にインターフェースを実装してるクラスのメソッド名変えてみたらコンパイルできなかったし、をを~、javaのインターフェースって束縛としては便利だよなと思ったり。言語の違いってなかなかおもろいなぁと感じた瞬間だったり。

あと、こういうやり方もあるらしい。

オブジェクトをハッシュデータ構造と透過に扱う際に、tie ハッシュのインターフェースで NEXTKEY() というメソッドを実装しますが、これがまさに iterator なので、

って部分がイマイチ飲み込めなったので、実用PerlプログラミングのTieのとこと型グロブの章をよく読んでなんとなく理解した。(理解した気になった?)

ProductName 実用Perlプログラミング
スリラム スリニバサン
オライリー・ジャパン / ?円 ( 1998-11 )


で、CPANもいくらか調べてみたところ、Iterator::Utilというのがよさげなのかな?この場合はiappendを使えばいいのかな?

明日コードを書いてみよう。

このエントリは結城浩さんの本と、それに載っているサンプルをPerlで書いた例をみながら考えたことをメモってます。あとは、はてなの伊藤さんのjavascriptでの例も参考にしてます。 間違いの指摘とか、もっといいやり方教授してくれたらとても嬉しいゾ。

ProductName 増補改訂版Java言語で学ぶデザインパターン入門
結城 浩
ソフトバンククリエイティブ / 3990円 ( 2004-06-19 )


Perlでデザインパターン

HeadFirstみても、なかなかperlでのデザインパターンが飲み込めないぁと悩んでいたら、結城 浩さんの本をいろんな言語で書いてるのを見つけた。 で、その中にPerlでデザインパターンも発見。はてなのえらいヒト?だよね。

というわけで、買ってみた。

ProductName 増補改訂版Java言語で学ぶデザインパターン入門
結城 浩
ソフトバンククリエイティブ / 3990円 ( 2004-06-19 )


AbstractFactoryくらいまで読んだが、名著の予感。

だって面白いんだもん

そして、perlのサンプル素敵、全部消化してから次のパターンに移ってる。で、もちろんjavaも書いて実行している(eclipseで)。

当分、この本の追っかけメモになるかも。本に関して一言いうならば、イタリック体はもうちょいイタリックっぽくして欲しかったね。いまんとこそれだけ。