1=encoding utf-8 2 3=for stopwords OO 4 5=head1 NAME 6 7Object::Container::ja - シンプルなオブジェクトコンテナインタフェース 8 9=head1 SYNOPSIS 10 11 use Object::Container; 12 13 ## OO インタフェース 14 # 初期化 15 my $container = Object::Container->new; 16 17 # クラスを登録 18 $container->register('HTML::TreeBuilder'); 19 20 # クラスをイニシャライザ指定して登録 21 $container->register('WWW::Mechanize', sub { 22 my $mech = WWW::Mechanize->new( stack_depth => 1 ); 23 $mech->agent_alias('Windows IE 6'); 24 return $mech; 25 }); 26 27 # 登録したオブジェクトを得る 28 my $mech = $container->get('WWW::Mechanize'); 29 30 ## Singletonインタフェース 31 my $container = Object::Container->instance; 32 33 # Singletonインタフェースの場合はregister/getはクラスメソッドとして動作する 34 Object::Container->register('WWW::Mechanize'); 35 my $mech = Object::Container->get('WWW::Mechanize'); 36 37 # Singletonインタフェースはget関数を任意の名前でエクスポートできる 38 use Object::Container 'container'; 39 container->register('WWW::Mechanize'); 40 my $mech = container->get('WWW::Mechanize'); 41 my $mech = container('WWW::Mechanize'); # save as above 42 43 # Singletonインタフェースのサブクラス化 44 package MyObj; 45 use Object::Container '-base'; 46 47 register 'ua' => sub { LWP::UserAgent->new }; 48 49=head1 DESCRIPTION 50 51Object::Container は Singleton インタフェース、OO インタフェースどちらでもつかえるシンプルなオブジェクトコンテナーを提供するモジュールです。 52 53アプリケーション中で同一のオブジェクトをいろいろな場所で使用したい場合があるかもしれません。 54そのような場合に、L<Class::Singleton> などを使用してどこからでもそのオブジェクトを取り出せるように設計することがありますが、この方法だと使用したいクラスをサブクラス化して使用する必要があります。 55 56このモジュールではオブジェクトを複数格納できるコンテナーを提供し、コンテナー自身を Singleton にすることで複数のオブジェクトを簡単にどこからでもアクセスできるようにすることができます。 57 58設計思想は L<Object::Registrar> というモジュールに似ていますが、OOインターフェースを持つ点、登録されたオブジェクトの初期化を実際に必要になるまで行わない (遅延実行)点が異なっています。 59 60=head2 OOインターフェースとSingletonインターフェース 61 62このモジュールは OO インターフェースと Singleton インタフェースの二種類のインターフェースを持ちます。 63 64OOインターフェスは 65 66 my $container = Object::Container->new; 67 68などのようにコンストラクタを呼び、その返り値のオブジェクトを介してオブジェクトの登録や取得を行います。この場合登録したオブジェクトはコンテナーオブジェクトごとに独立しています。 69 70例えば 71 72 my $container1 = Object::Container->new; 73 my $container2 = Object::Container->new; 74 75などのように複数のコンテナーを使い分けるような使い方ができます。 76 77Singletonインタフェースは 78 79 my $container = Object::Container->instance; 80 81というように明示的にコンストラクタをよばす、クラスに割り当てられた唯一のオブジェクトを使用するインターフェースです。 82 83Singletonインタフェースを使用する場合は、register や get 関数などは 84 85 Object::Container->register('WWW::Mechanize', sub { WWW::Mechanize->new( stack_depth => 1 ) }); 86 87というようにすべてクラスメソッドとして使用することができます。Singletonインターフェースで複数のコンテナーを使いたい場合はサブクラス化をして 88 89 MyContainer1->get('WWW::Mechanize'); 90 MyContainer2->get('WWW::Mechanize'); 91 92のようにします。 93 94=head2 SingletonインタフェースとEXPORT関数 95 96Singletonインタフェースで、いちいち 97 98 MyContainer->get('WWW::Mechanize'); 99 100と書くのがだるい、と言う人のために好きな名前でコンテナをEXPORTできる機能を用意してあります。 101 102 use MyContainer 'obj'; 103 104と、use 時にエクスポートしたい関数名を指定します。すると 105 106 obj->register( mech => sub { WWW::Mechanize->new }); 107 108 obj->get('mech'); 109 obj('mech'); # shortcut to obj->get('mech') 110 111などと短い書き方でコンテナーにアクセスできるようになります。 112 113=head2 Singletonインタフェースとサブクラス化 114 115Singletonインタフェースのサブクラス内でオブジェクトを登録したいときに 116 117 __PACKAGE__->register( mech => sub { WWW::Mechanize->new } ); 118 119と書くのがだるい、と言う人のためにサブクラス化時のインタフェースも用意してあります。 120 121サブクラス化するときに、 122 123 use base 'Object::Container'; 124 125とするかわりに 126 127 use Object::Container '-base'; 128 129とすると register と言う関数がエクスポートされます。こうすると上記の C<< __PACKAGE__->register >> のかわりに 130 131 register mech => sub { WWW::Mechanize->new }; 132 133と書くことができるようになります。 134 135=head2 遅延ロードと依存性解決 136 137registerメソッドで登録されたオブジェクトは、初回の get メソッドを実行したときに初めて初期化されます。 138 139 Object::Container->register('WWW::Mechanize', sub { WWW::Mechanize->new }); # ここで WWW::Mechanize->new は実行されない 140 my $mech = Object::Container->get('WWW::Mechanize'); # ここで実行される 141 142この機能により大量にクラスが登録されていても、必要な物のみ初期化されるためリソースを大量に消費することがないため永続プロセス以外でも手軽に導入できるでしょう。 143 144また Singleton インタフェースは初期化関数と組み合わせることにより、オブジェクト同士の依存性の解決も行うことができます。 145 146たとえば、L<HTTP::Cookies> オブジェクトに依存した L<LWP::UserAgent> を考えます。このような場合、 147 148 Object::Container->register('HTTP::Cookies', sub { HTTP::Cookies->new( file => '/path/to/cookie.dat' ) }); 149 Object::Container->register('LWP::UserAgent', sub { 150 my $cookies = Object::Container->get('HTTP::Cookies'); 151 LWP::UserAgent->new( cookie_jar => $cookies ); 152 }); 153 154というように初期化関数のなかで get メソッドをしようすることで依存性の解決が行えます。 155 156上記の場合、 157 158 my $ua = Object::Container->get('LWP::UserAgent'); 159 160した場合に LWP::UserAgent と HTTP::Cookies の両方が初期化されます。 161 162もし、登録と同時に初期化したい場合、以下のようにできます。 163 164 Object::Container->register({ class => 'LWP::UserAgent', preload => 1 }); 165 166I<initializer> オプションを指定することができます。 167 168 Object::Container->register({ class => 'WWW::Mechanize', initializer => sub { 169 my $mech = WWW::Mechanize->new( stack_depth ); 170 $mech->agent_alias('Windows IE 6'); 171 return $mech; 172 }, preload => 1 }); 173 174これは、以下のように書くのと同じです。 175 176 Object::Container->register('WWW::Mechanize', sub { 177 my $mech = WWW::Mechanize->new( stack_depth ); 178 $mech->agent_alias('Windows IE 6'); 179 return $mech; 180 }); 181 Object::Container->get('WWW::Mechanize'); 182 183I<args> オプションを指定した場合は: 184 185 Object::Container->register({ class => 'LWP::UserAgent', args => \@args, preload => 1 }); 186 187これは、もうおわかりのように、以下と同じです。 188 189 Object::Container->register('LWP::UserAgent', @args); 190 Object::Container->get('LWP::UserAgent'); 191 192=head1 METHODS 193 194=head2 register( $class, @args ) 195 196=head2 register( $class_or_name, $initialize_code ) 197 198Object::Container にオブジェクトを登録します。 199 200いちばんシンプルな使い方は 201 202 Object::Container->register('WWW::Mechanize'); 203 204などのようにクラス名のみを登録する方法です。この場合 get した場合に WWW::Mechanize->new が引数なしで呼ばれます。 205 206new の引数を指定したい場合は 207 208 Object::Container->register('WWW::Mechanize', @constructor_args); 209 210などのように第二引数以降に配列をわたせばそれがそのまま new にわたされます。 211 212new 以外のコンストラクタが必要な場合、他に初期化処理が必要な場合、依存しているモジュールがある場合などは、第二引数にコードリファレンスを渡すことで任意の初期化処理が行えます。 213 214 Object::Container->register('WWW::Mechanize', sub { 215 my $mech = WWW::Mechanize->new( stack_depth ); 216 $mech->agent_alias('Windows IE 6'); 217 return $mech; 218 }); 219 220このコードリファレンスではコンテナに格納するオブジェクトを返す必要があります。 221 222またこのように初期化関数を渡す場合、第一引数ではクラス名を与える必要はなく任意の名前を与えることができます。 223 224 Object::Container->register('ua1', sub { LWP::UserAgent->new }); 225 Object::Container->register('ua2', sub { LWP::UserAgent->new }); 226 227などと言った使い方も可能です。 228 229=head2 get($class_or_name) 230 231registerメソッドで登録したオブジェクトを取得します。 232 233与える引数はregisterメソッドに与えた第一引数と同じ物を渡します。 234 235=head2 ensure_class_loaded($class) 236 237$class がロードされているか確認し、ロードされていなかった場合そのクラスを use してくれるユーティリティ関数です。 238 239初期化関数に依存性を含ませるような場合でその依存モジュールを遅延ロードしたい場合などに使用すると便利です。 240 241=head2 load_all 242 243=head2 load_all_except(@classes_or_names) 244 245基本的にこのモジュールは必要になるまで(getメソッドが呼ばれるまで)オブジェクトを初期化しませんが、 246C<Copy-On-Write> や、実行時の速度を重視する場合など、あらかじめオブジェクトを初期化しておきたい場合があるかもしれません。そのような場合には 247 248 Object::Container->load_all; 249 250とすることで全てのオブジェクトを初期化済みにすることができます。 251 252また、特定のオブジェクトだけは初期化したくないという場合、 253 254 Object::Container->load_all_except(qw/Foo Bar/); 255 256などとすると初期化したくないオブジェクト以外の全てのオブジェクトを初期化することも出来ます。 257上記の場合は Foo と Bar と言うオブジェクト以外の全てのオブジェクトを初期化します。 258 259=head1 EXPORT FUNCTIONS ON SUBCLASS INTERFACE 260 261 package MyContainer; 262 use strict; 263 use warnings; 264 use Object::Container '-base'; 265 266とすることで Object::Container を継承し独自のコンテナークラスを定義することが出来ます。 267 268このサブクラス中では以下の関数をしようしてオブジェクト定義をすることができます。 269 270=head2 register( $class, @args ) 271 272=head2 register( $class_or_name, $initialize_code ) 273 274 register Foo => sub { 275 my ($self) = @_; 276 $self->ensure_class_loaded('Foo'); 277 Foo->new; 278 }; 279 280オブジェクトを登録します。上述したクラス(オブジェクト)メソッドの C<register> メソッドとおなじ役割をします。 281 282=head2 preload(@classes_or_names) 283 284=head2 preload_all 285 286=head2 preload_all_except 287 288これらはクラス(オブジェクト)メソッドの C<load_all>、C<load_all_except> と同じようにつかえる関数で、その名前の通り C<preload_all> が C<load_all> と、C<preload_all_except> が C<load_all_except> とそれぞれ対応しています。 289 290=head1 SEE ALSO 291 292L<Class::Singleton>, L<Object::Registrar>. 293 294=head1 AUTHOR 295 296Daisuke Murase <typester@cpan.org> 297 298=head1 COPYRIGHT & LICENSE 299 300Copyright (c) 2009 by KAYAC Inc. 301 302This program is free software; you can redistribute 303it and/or modify it under the same terms as Perl itself. 304 305The full text of the license can be found in the 306LICENSE file included with this module. 307 308=cut 309