1Object::Realize::Later(U3s)er Contributed Perl DocumentatOibojnect::Realize::Later(3)
2
3
4
6 Object::Realize::Later - Delayed creation of objects
7
9 package MyLazyObject;
10
11 use Object::Realize::Later
12 becomes => 'MyRealObject',
13 realize => 'load';
14
16 The "Object::Realize::Later" class helps with implementing transparent
17 on demand realization of object data. This is related to the tricks on
18 autoloading of data, the lesser known cousin of autoloading of
19 functionality.
20
21 On demand realization is all about performance gain. Why should you
22 spent costly time on realizing an object, when the data on the object
23 is never (or not yet) used? In interactive programs, postponed
24 realization may boost start-up: the realization of objects is triggered
25 by the use, so spread over time.
26
28 Construction
29 use(Object::Realize::Later %options)
30 When you invoke ("use") the "Object::Realize::Later" package, it
31 will add a set of methods to your package (see section "Added to
32 YOUR class").
33
34 -Option --Default
35 becomes <required>
36 believe_caller <false>
37 realize <required>
38 source_module <becomes>
39 warn_realization <false>
40 warn_realize_again <false>
41
42 becomes => CLASS
43 Which type will this object become after realization.
44
45 believe_caller => BOOLEAN
46 When a method is called on the un-realized object, the AUTOLOAD
47 checks whether this resolves the need. If not, the realization
48 is not done. However, when realization may result in an object
49 that extends the functionality of the class specified with
50 "becomes", this check must be disabled. In that case, specify
51 true for this option.
52
53 realize => METHOD|CODE
54 How will transform. If you specify a CODE reference, then this
55 will be called with the lazy-object as first argument, and the
56 requested method as second.
57
58 After realization, you may still have your hands on the lazy
59 object on various places. Be sure that your realization method
60 is coping with that, for instance by using Memoize. See examples
61 below.
62
63 source_module => CLASS
64 if the class (a package) is included in a file (module) with a
65 different name, then use this argument to specify the file name.
66 The name is expected to be the same as in the "require" call
67 which would load it.
68
69 warn_realization => BOOLEAN
70 Print a warning message when the realization starts. This is for
71 debugging purposes.
72
73 warn_realize_again => BOOLEAN
74 When an object is realized, the original object -which functioned
75 as a stub- is reconstructed to work as proxy to the realized
76 object. This option will issue a warning when that proxy is
77 used, which means that somewhere in your program there is a
78 variable still holding a reference to the stub. This latter is
79 not problematic at all, although it slows-down each method call.
80
81 Added to YOUR class
82 $obj->AUTOLOAD()
83 When a method is called which is not available for the lazy object,
84 the AUTOLOAD is called.
85
86 $obj->can($method)
87 Object::Realize::Later->can($method)
88 Is the specified $method available for the lazy or the realized
89 version of this object? It will return the reference to the code.
90
91 example:
92
93 MyLazyObject->can('lazyWork') # true
94 MyLazyObject->can('realWork') # true
95
96 my $lazy = MyLazyObject->new;
97 $lazy->can('lazyWork'); # true
98 $lazy->can('realWork'); # true
99
100 $obj->forceRealize()
101 You can force the load by calling this method on your object. It
102 returns the realized object.
103
104 Object::Realize::Later->isa($class)
105 Is this object a (sub-)class of the specified $class or can it
106 become a (sub-)class of $class.
107
108 example:
109
110 MyLazyObject->isa('MyRealObject') # true
111 MyLazyObject->isa('SuperClassOfLazy'); # true
112 MyLazyObject->isa('SuperClassOfReal'); # true
113
114 my $lazy = MyLazyObject->new;
115 $lazy->isa('MyRealObject'); # true
116 $lazy->isa('SuperClassOfLazy'); # true
117 $lazy->isa('SuperClassOfReal'); # true
118
119 $obj->willRealize()
120 Returns which class will be the realized to follow-up this class.
121
122 Object::Realize::Later internals
123 The next methods are not exported to the class where the `use' took
124 place. These methods implement the actual realization.
125
126 Object::Realize::Later->import(%options)
127 The %options used for "import" are the values after the class name
128 with "use". So this routine implements the actual option parsing.
129 It generates code dynamically, which is then evaluated in the
130 callers name-space.
131
132 Object::Realize::Later->realizationOf( $object, [$realized] )
133 Returns the $realized version of $object, optionally after setting
134 it first. When the method returns "undef", the realization has not
135 yet taken place or the realized object has already been removed
136 again.
137
138 Object::Realize::Later->realize(%options)
139 This method is called when a "$object-"forceRealize()> takes place.
140 It checks whether the realization has been done already (is which
141 case the realized object is returned)
142
144 About lazy loading
145 There are two ways to implement lazy behaviour: you may choose to check
146 whether you have realized the data in each method which accesses the
147 data, or use the autoloading of data trick.
148
149 An implementation of the first solution is:
150
151 sub realize {
152 my $self = shift;
153 return $self unless $self->{_is_realized};
154
155 # read the data from file, or whatever
156 $self->{data} = ....;
157
158 $self->{_is_realized} = 1;
159 $self;
160 }
161
162 sub getData() {
163 my $self = shift;
164 return $self->realize->{data};
165 }
166
167 The above implementation is error-prone, where you can easily forget to
168 call realize(). The tests cannot cover all ordenings of method-calls
169 to detect the mistakes.
170
171 The second approach uses autoloading, and is supported by this package.
172 First we create a stub-object, which will be transformable into a
173 realized object later. This transformation is triggered by AUTOLOAD.
174
175 This stub-object may contain some methods from the realized object, to
176 reduce the need for realization. The stub will also contain some
177 information which is required for the creation of the real object.
178
179 "Object::Realize::Later" solves the inheritance problems (especially
180 the isa() and can() methods) and supplies the AUTOLOAD method. Class
181 methods which are not defined in the stub object are forwarded as class
182 methods without realization.
183
184 Traps
185 Be aware of dangerous traps in the current implementation. These
186 problems appear by having multiple references to the same delayed
187 object. Depending on how the realization is implemented, terrible
188 things can happen.
189
190 The two versions of realization:
191
192 · by reblessing
193
194 This is the safe version. The realized object is the same object
195 as the delayed one, but reblessed in a different package. When
196 multiple references to the delayed object exists, they will all be
197 updated at the same, because the bless information is stored within
198 the refered variable.
199
200 · by new instance
201
202 This is the nicest way of realization, but also quite more
203 dangerous. Consider this:
204
205 package Delayed;
206 use Object::Realize::Later
207 becomes => 'Realized',
208 realize => 'load';
209
210 sub new($) {my($class,$v)=@_; bless {label=>$v}, $class}
211 sub setLabel($) {my $self = shift; $self->{label} = shift}
212 sub load() {$_[0] = Realized->new($_[0]->{label}) }
213
214 package Realized; # file Realized.pm or use use(source_module)
215 sub new($) {my($class,$v)=@_; bless {label=>$v}, $class}
216 sub setLabel($) {my $self = shift; $self->{label} = shift}
217 sub getLabel() {my $self = shift; $self->{label}}
218
219 package main;
220 my $original = Delayed->new('original');
221 my $copy = $original;
222 print $original->getLabel; # prints 'original'
223 print ref $original; # prints 'Realized'
224 print ref $copy; # prints 'Delayed'
225 $original->setLabel('changed');
226 print $original->getLabel; # prints 'changed'
227 print $copy->getLabel; # prints 'original'
228
229 Examples
230 Example 1
231
232 In the first example, we delay-load a message. On the moment the
233 message is defined, we only take the location. When the data of the
234 message is taken (header or body), the data is autoloaded.
235
236 package Mail::Message::Delayed;
237
238 use Object::Realize::Later
239 ( becomes => 'Mail::Message::Real'
240 , realize => 'loadMessage'
241 );
242
243 sub new($) {
244 my ($class, $file) = @_;
245 bless { filename => $file }, $class;
246 }
247
248 sub loadMessage() {
249 my $self = shift;
250 Mail::Message::Real->new($self->{filename});
251 }
252
253 In the main program:
254
255 package main;
256 use Mail::Message::Delayed;
257
258 my $msg = Mail::Message::Delayed->new('/home/user/mh/1');
259 $msg->body->print; # this will trigger autoload.
260
261 Example 2
262
263 Your realization may also be done by reblessing. In that case to
264 change the type of your object into a different type which stores the
265 same information. Is that right? Are you sure? For simple cases,
266 this may be possible:
267
268 package Alive;
269 use Object::Realize::Later
270 becomes => 'Dead',
271 realize => 'kill';
272
273 sub new() {my $class = shift; bless {@_}, $class}
274 sub jump() {print "Jump!\n"}
275 sub showAntlers() {print "Fight!\n"}
276 sub kill() {bless(shift, 'Dead')}
277
278 package Dead;
279 sub takeAntlers() {...}
280
281 In the main program:
282
283 my $deer = Alive->new(Animal => 'deer');
284 my $trophy = $deer->takeAntlers();
285
286 In this situation, the object (reference) is not changed but is
287 reblessed. There is no danger that the un-realized version of the
288 object is kept somewhere: all variable which know about this partical
289 deer see the change.
290
291 Example 3
292
293 This module is especially useful for larger projects, which there is a
294 need for speed or memory reduction. In this case, you may have an extra
295 overview on which objects have been realized (transformed), and which
296 not. This example is taken from the MailBox modules:
297
298 The Mail::Box module tries to boost the access-time to a folder. If
299 you only need the messages of the last day, why shall all be read? So,
300 MailBox only creates an invertory of messages at first. It takes the
301 headers of all messages, but leaves the body (content) of the message
302 in the file.
303
304 In MailBox' case, the Mail::Message-object has the choice between a
305 number of Mail::Message::Body's, one of which has only be prepared to
306 read the body when needed. A code snippet:
307
308 package Mail::Message;
309 sub new($$)
310 { my ($class, $head, $body) = @_;
311 my $self = bless {head => $head, body => $body}, $class;
312 $body->message($self); # tell body about the message
313 }
314 sub head() { shift->{head} }
315 sub body() { shift->{body} }
316
317 sub loadBody()
318 { my $self = shift;
319 my $body = $self->body;
320
321 # Catch re-invocations of the loading. If anywhere was still
322 # a reference to the old (unrealized) body of this message, we
323 # return the new-one directly.
324 return $body unless $body->can('forceRealize');
325
326 # Load the body (change it to anything which really is of
327 # the promised type, or a sub-class of it.
328 my ($lines, $size) = .......; # get the data
329 $self->{body} = Mail::Message::Body::Lines
330 ->new($lines, $size, $self);
331
332 # Return the realized object.
333 return $self->{body};
334 }
335
336 package Mail::Message::Body::Lines;
337 use base 'Mail::Message::Body';
338
339 sub new($$$)
340 { my ($class, $lines, $size, $message) = @_;
341 bless { lines => $lines, size => $size
342 , message => $message }, $class;
343 }
344 sub size() { shift->{size} }
345 sub lines() { shift->{lines} }
346 sub message() { shift->{message);
347
348 package Mail::Message::Body::Delayed;
349 use Object::Realize::Later
350 becomes => 'Mail::Message::Body',
351 realize => sub {shift->message->loadBody};
352
353 sub new($)
354 { my ($class, $size) = @_;
355 bless {size => $size}, $class;
356 }
357 sub size() { shift->{size} }
358 sub message(;$)
359 { my $self = shift;
360 @_ ? ($self->{message} = shift) : $self->{messages};
361 }
362
363 package main;
364 use Mail::Message;
365 use Mail::Message::Body::Delayed;
366
367 my $body = Mail::Message::Body::Delayed->new(42);
368 my $message = Mail::Message->new($head, $body);
369
370 print $message->size; # will not trigger realization!
371 print $message->can('lines'); # true, but no realization yet.
372 print $message->lines; # realizes automatically.
373
375 This module is part of Object-Realize-Later distribution version 0.21,
376 built on January 24, 2018. Website: http://perl.overmeer.net/CPAN/
377
379 Copyrights 2001-2018 by [Mark Overmeer]. For other contributors see
380 ChangeLog.
381
382 This program is free software; you can redistribute it and/or modify it
383 under the same terms as Perl itself. See http://dev.perl.org/licenses/
384
385
386
387perl v5.32.0 2020-07-28 Object::Realize::Later(3)