Commit | Line | Data |
a0d0e21e |
1 | =head1 NAME |
2 | |
3 | perlbot - Bag'o Object Tricks For Perl5 (the BOT) |
4 | |
5 | =head1 INTRODUCTION |
6 | |
7 | The following collection of tricks and hints is intended to whet curious |
8 | appetites about such things as the use of instance variables and the |
9 | mechanics of object and class relationships. The reader is encouraged to |
10 | consult relevant textbooks for discussion of Object Oriented definitions and |
11 | methodology. This is not intended as a comprehensive guide to Perl5's |
12 | object oriented features, nor should it be construed as a style guide. |
13 | |
14 | The Perl motto still holds: There's more than one way to do it. |
15 | |
16 | =head1 INSTANCE VARIABLES |
17 | |
18 | An anonymous array or anonymous hash can be used to hold instance |
19 | variables. Named parameters are also demonstrated. |
20 | |
21 | package Foo; |
22 | |
23 | sub new { |
24 | my $type = shift; |
25 | my %params = @_; |
26 | my $self = {}; |
27 | $self->{'High'} = $params{'High'}; |
28 | $self->{'Low'} = $params{'Low'}; |
29 | bless $self; |
30 | } |
31 | |
32 | |
33 | package Bar; |
34 | |
35 | sub new { |
36 | my $type = shift; |
37 | my %params = @_; |
38 | my $self = []; |
39 | $self->[0] = $params{'Left'}; |
40 | $self->[1] = $params{'Right'}; |
41 | bless $self; |
42 | } |
43 | |
44 | package main; |
45 | |
46 | $a = new Foo ( 'High' => 42, 'Low' => 11 ); |
47 | print "High=$a->{'High'}\n"; |
48 | print "Low=$a->{'Low'}\n"; |
49 | |
50 | $b = new Bar ( 'Left' => 78, 'Right' => 40 ); |
51 | print "Left=$b->[0]\n"; |
52 | print "Right=$b->[1]\n"; |
53 | |
54 | |
55 | =head1 SCALAR INSTANCE VARIABLES |
56 | |
57 | An anonymous scalar can be used when only one instance variable is needed. |
58 | |
59 | package Foo; |
60 | |
61 | sub new { |
62 | my $type = shift; |
63 | my $self; |
64 | $self = shift; |
65 | bless \$self; |
66 | } |
67 | |
68 | package main; |
69 | |
70 | $a = new Foo 42; |
71 | print "a=$$a\n"; |
72 | |
73 | |
74 | =head1 INSTANCE VARIABLE INHERITANCE |
75 | |
76 | This example demonstrates how one might inherit instance variables from a |
77 | superclass for inclusion in the new class. This requires calling the |
78 | superclass's constructor and adding one's own instance variables to the new |
79 | object. |
80 | |
81 | package Bar; |
82 | |
83 | sub new { |
84 | my $self = {}; |
85 | $self->{'buz'} = 42; |
86 | bless $self; |
87 | } |
88 | |
89 | package Foo; |
90 | @ISA = qw( Bar ); |
91 | |
92 | sub new { |
93 | my $self = new Bar; |
94 | $self->{'biz'} = 11; |
95 | bless $self; |
96 | } |
97 | |
98 | package main; |
99 | |
100 | $a = new Foo; |
101 | print "buz = ", $a->{'buz'}, "\n"; |
102 | print "biz = ", $a->{'biz'}, "\n"; |
103 | |
104 | |
105 | |
106 | =head1 OBJECT RELATIONSHIPS |
107 | |
108 | The following demonstrates how one might implement "containing" and "using" |
109 | relationships between objects. |
110 | |
111 | package Bar; |
112 | |
113 | sub new { |
114 | my $self = {}; |
115 | $self->{'buz'} = 42; |
116 | bless $self; |
117 | } |
118 | |
119 | package Foo; |
120 | |
121 | sub new { |
122 | my $self = {}; |
123 | $self->{'Bar'} = new Bar (); |
124 | $self->{'biz'} = 11; |
125 | bless $self; |
126 | } |
127 | |
128 | package main; |
129 | |
130 | $a = new Foo; |
131 | print "buz = ", $a->{'Bar'}->{'buz'}, "\n"; |
132 | print "biz = ", $a->{'biz'}, "\n"; |
133 | |
134 | |
135 | |
136 | =head1 OVERRIDING SUPERCLASS METHODS |
137 | |
138 | The following example demonstrates how one might override a superclass |
139 | method and then call the method after it has been overridden. The |
140 | Foo::Inherit class allows the programmer to call an overridden superclass |
141 | method without actually knowing where that method is defined. |
142 | |
143 | |
144 | package Buz; |
145 | sub goo { print "here's the goo\n" } |
146 | |
147 | package Bar; @ISA = qw( Buz ); |
148 | sub google { print "google here\n" } |
149 | |
150 | package Baz; |
151 | sub mumble { print "mumbling\n" } |
152 | |
153 | package Foo; |
154 | @ISA = qw( Bar Baz ); |
155 | @Foo::Inherit::ISA = @ISA; # Access to overridden methods. |
156 | |
157 | sub new { bless [] } |
158 | sub grr { print "grumble\n" } |
159 | sub goo { |
160 | my $self = shift; |
161 | $self->Foo::Inherit::goo(); |
162 | } |
163 | sub mumble { |
164 | my $self = shift; |
165 | $self->Foo::Inherit::mumble(); |
166 | } |
167 | sub google { |
168 | my $self = shift; |
169 | $self->Foo::Inherit::google(); |
170 | } |
171 | |
172 | package main; |
173 | |
174 | $foo = new Foo; |
175 | $foo->mumble; |
176 | $foo->grr; |
177 | $foo->goo; |
178 | $foo->google; |
179 | |
180 | |
181 | =head1 USING RELATIONSHIP WITH SDBM |
182 | |
183 | This example demonstrates an interface for the SDBM class. This creates a |
184 | "using" relationship between the SDBM class and the new class Mydbm. |
185 | |
186 | use SDBM_File; |
187 | use POSIX; |
188 | |
189 | package Mydbm; |
190 | |
191 | sub TIEHASH { |
192 | my $self = shift; |
193 | my $ref = SDBM_File->new(@_); |
194 | bless {'dbm' => $ref}; |
195 | } |
196 | sub FETCH { |
197 | my $self = shift; |
198 | my $ref = $self->{'dbm'}; |
199 | $ref->FETCH(@_); |
200 | } |
201 | sub STORE { |
202 | my $self = shift; |
203 | if (defined $_[0]){ |
204 | my $ref = $self->{'dbm'}; |
205 | $ref->STORE(@_); |
206 | } else { |
207 | die "Cannot STORE an undefined key in Mydbm\n"; |
208 | } |
209 | } |
210 | |
211 | package main; |
212 | |
213 | tie %foo, Mydbm, "Sdbm", O_RDWR|O_CREAT, 0640; |
214 | $foo{'bar'} = 123; |
215 | print "foo-bar = $foo{'bar'}\n"; |
216 | |
217 | tie %bar, Mydbm, "Sdbm2", O_RDWR|O_CREAT, 0640; |
218 | $bar{'Cathy'} = 456; |
219 | print "bar-Cathy = $bar{'Cathy'}\n"; |
220 | |
221 | =head1 THINKING OF CODE REUSE |
222 | |
223 | One strength of Object-Oriented languages is the ease with which old code |
224 | can use new code. The following examples will demonstrate first how one can |
225 | hinder code reuse and then how one can promote code reuse. |
226 | |
227 | This first example illustrates a class which uses a fully-qualified method |
228 | call to access the "private" method BAZ(). The second example will show |
229 | that it is impossible to override the BAZ() method. |
230 | |
231 | package FOO; |
232 | |
233 | sub new { bless {} } |
234 | sub bar { |
235 | my $self = shift; |
236 | $self->FOO::private::BAZ; |
237 | } |
238 | |
239 | package FOO::private; |
240 | |
241 | sub BAZ { |
242 | print "in BAZ\n"; |
243 | } |
244 | |
245 | package main; |
246 | |
247 | $a = FOO->new; |
248 | $a->bar; |
249 | |
250 | Now we try to override the BAZ() method. We would like FOO::bar() to call |
d1b91892 |
251 | GOOP::BAZ(), but this cannot happen because FOO::bar() explicitly calls |
a0d0e21e |
252 | FOO::private::BAZ(). |
253 | |
254 | package FOO; |
255 | |
256 | sub new { bless {} } |
257 | sub bar { |
258 | my $self = shift; |
259 | $self->FOO::private::BAZ; |
260 | } |
261 | |
262 | package FOO::private; |
263 | |
264 | sub BAZ { |
265 | print "in BAZ\n"; |
266 | } |
267 | |
268 | package GOOP; |
269 | @ISA = qw( FOO ); |
270 | sub new { bless {} } |
271 | |
272 | sub BAZ { |
273 | print "in GOOP::BAZ\n"; |
274 | } |
275 | |
276 | package main; |
277 | |
278 | $a = GOOP->new; |
279 | $a->bar; |
280 | |
281 | To create reusable code we must modify class FOO, flattening class |
282 | FOO::private. The next example shows a reusable class FOO which allows the |
283 | method GOOP::BAZ() to be used in place of FOO::BAZ(). |
284 | |
285 | package FOO; |
286 | |
287 | sub new { bless {} } |
288 | sub bar { |
289 | my $self = shift; |
290 | $self->BAZ; |
291 | } |
292 | |
293 | sub BAZ { |
294 | print "in BAZ\n"; |
295 | } |
296 | |
297 | package GOOP; |
298 | @ISA = qw( FOO ); |
299 | |
300 | sub new { bless {} } |
301 | sub BAZ { |
302 | print "in GOOP::BAZ\n"; |
303 | } |
304 | |
305 | package main; |
306 | |
307 | $a = GOOP->new; |
308 | $a->bar; |
309 | |
310 | =head1 CLASS CONTEXT AND THE OBJECT |
311 | |
312 | Use the object to solve package and class context problems. Everything a |
313 | method needs should be available via the object or should be passed as a |
314 | parameter to the method. |
315 | |
316 | A class will sometimes have static or global data to be used by the |
317 | methods. A subclass may want to override that data and replace it with new |
318 | data. When this happens the superclass may not know how to find the new |
319 | copy of the data. |
320 | |
321 | This problem can be solved by using the object to define the context of the |
322 | method. Let the method look in the object for a reference to the data. The |
323 | alternative is to force the method to go hunting for the data ("Is it in my |
324 | class, or in a subclass? Which subclass?"), and this can be inconvenient |
325 | and will lead to hackery. It is better to just let the object tell the |
326 | method where that data is located. |
327 | |
328 | package Bar; |
329 | |
330 | %fizzle = ( 'Password' => 'XYZZY' ); |
331 | |
332 | sub new { |
333 | my $self = {}; |
334 | $self->{'fizzle'} = \%fizzle; |
335 | bless $self; |
336 | } |
337 | |
338 | sub enter { |
339 | my $self = shift; |
340 | |
341 | # Don't try to guess if we should use %Bar::fizzle |
342 | # or %Foo::fizzle. The object already knows which |
343 | # we should use, so just ask it. |
344 | # |
345 | my $fizzle = $self->{'fizzle'}; |
346 | |
347 | print "The word is ", $fizzle->{'Password'}, "\n"; |
348 | } |
349 | |
350 | package Foo; |
351 | @ISA = qw( Bar ); |
352 | |
353 | %fizzle = ( 'Password' => 'Rumple' ); |
354 | |
355 | sub new { |
356 | my $self = Bar->new; |
357 | $self->{'fizzle'} = \%fizzle; |
358 | bless $self; |
359 | } |
360 | |
361 | package main; |
362 | |
363 | $a = Bar->new; |
364 | $b = Foo->new; |
365 | $a->enter; |
366 | $b->enter; |
367 | |
d1b91892 |
368 | =head1 INHERITING A CONSTRUCTOR |
369 | |
370 | An inheritable constructor should use the second form of bless() which allows |
371 | blessing directly into a specified class. Notice in this example that the |
372 | object will be a BAR not a FOO, even though the constructor is in class FOO. |
373 | |
374 | package FOO; |
375 | |
376 | sub new { |
377 | my $type = shift; |
378 | my $self = {}; |
379 | bless $self, $type; |
380 | } |
381 | |
382 | sub baz { |
383 | print "in FOO::baz()\n"; |
384 | } |
385 | |
386 | package BAR; |
387 | @ISA = qw(FOO); |
388 | |
389 | sub baz { |
390 | print "in BAR::baz()\n"; |
391 | } |
392 | |
393 | package main; |
394 | |
395 | $a = BAR->new; |
396 | $a->baz; |
397 | |
398 | =head1 DELEGATION |
399 | |
400 | Some classes, such as SDBM_File, cannot be effectively subclassed because |
401 | they create foreign objects. Such a class can be extended with some sort of |
402 | aggregation technique such as the "using" relationship mentioned earlier or |
403 | by delegation. |
404 | |
405 | The following example demonstrates delegation using an AUTOLOAD() function to |
406 | perform message-forwarding. This will allow the Mydbm object to behave |
407 | exactly like an SDBM_File object. The Mydbm class could now extend the |
408 | behavior by adding custom FETCH() and STORE() methods, if this is desired. |
409 | |
410 | package Mydbm; |
411 | |
412 | require SDBM_File; |
413 | require TieHash; |
414 | @ISA = qw(TieHash); |
415 | |
416 | sub TIEHASH { |
417 | my $type = shift; |
418 | my $ref = SDBM_File->new(@_); |
419 | bless {'delegate' => $ref}; |
420 | } |
421 | |
422 | sub AUTOLOAD { |
423 | my $self = shift; |
424 | |
425 | # The Perl interpreter places the name of the |
426 | # message in a variable called $AUTOLOAD. |
427 | |
428 | # DESTROY messages should never be propagated. |
429 | return if $AUTOLOAD =~ /::DESTROY$/; |
430 | |
431 | # Remove the package name. |
432 | $AUTOLOAD =~ s/^Mydbm:://; |
433 | |
434 | # Pass the message to the delegate. |
435 | $self->{'delegate'}->$AUTOLOAD(@_); |
436 | } |
437 | |
438 | package main; |
439 | use Fcntl qw( O_RDWR O_CREAT ); |
440 | |
441 | tie %foo, Mydbm, "adbm", O_RDWR|O_CREAT, 0640; |
442 | $foo{'bar'} = 123; |
443 | print "foo-bar = $foo{'bar'}\n"; |