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