Singleton

今日はSingletonです。一昨日くらいにやったんだけど実装してからなんだかんだと調べていたので今日になってしまいました。Singletonはインスタンス生成を1個だけに限定するためのパターンです。2回目以降は1回目に生成したインスタンスを返します。
クラスが一個しかないからラクかなと思っていたらぜんぜんそんなことなかった。ちょっとした壁にぶつかった模様。Perlのオブジェクトの作り方から言って厳密にSingletonを実装するのは難しい。Perlではリファレンスをクラス名にblessするとオブジェクトを作れて、通常コンストラクタでこれを行う。なぜなら、

Class->method;

とやるとClass.pmに定義されているmethodの第1引数に"Class"という文字列が渡されるからだ。したがって、

sub new {
    my $class = shift;
    bless {}, $class;
}

このようにnewというサブルーチン内でblessすればこれがコンストラクタになる。Perlではコンストラクタの名前は決まっていないのでnewという名前じゃなくてもぜんぜんかまわない。たとえばDBIというモジュールではconnectがコンストラクタだ。でも慣習でnewを使うことが多い。また、メソッドをプライベートにはできない(というかそもそもそういう考え方がない)。
まあつまり、単純なクラスの作り方じゃダメ。というわけで、ファイルスコープのレキシカル変数を使います。で、コンストラクタを使う場合はまずそのレキシカル変数がblessされてるか見て、されてたらそれを返すだけ、されてなかったらblessして返すとする。こうすれば大丈夫そうだ。でも教科書(増補改訂版Java言語で学ぶデザインパターン入門)のP53ではJavaの実装でこの方法は厳密にはSingletonにはならないと言っています。
そして私の最初の実装はこうなりました。
http://svn.takatoshi.dyndns.org/public/pattern/trunk/singleton/

package Singleton;
use strict;

BEGIN {
	my $singleton = bless {}, __PACKAGE__;
	print "インスタンスを生成しました。?n";
	sub get_instance {
		return $singleton;
	}
}

1;

ちょっとダサイ。継承できない。でも複数のインスタンス生成はできない。
ところで、このパターンの使い道は?つまり、インスタンスを1個に限定したい局面はどこか。

  • グローバルレベルのリソース(コンテキストやステートを持ってるものは不可)
  • DBハンドル(何回も接続しないように)

この二つが、「あー、そうね」と納得したところ。

  • Print Spooler
  • System Registry

この二つは「いわれてみるとそうかも」と思ったところ。スプーラもレジストリもシステムに唯一の存在だからかね。
こんな感じで、意外に手間取りました。

    • -


Class::Singleton - Implementation of a "Singleton" class - metacpan.org
Perl Design Patterns