Commit | Line | Data |
a0d0e21e |
1 | =head1 NAME |
2 | |
c2960299 |
3 | perlbot - Bag'o Object Tricks (the BOT) |
a0d0e21e |
4 | |
cb1a09d0 |
5 | =head1 DESCRIPTION |
a0d0e21e |
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 |
c2960299 |
11 | methodology. This is not intended as a tutorial for object-oriented |
12 | programming or as a comprehensive guide to Perl's object oriented features, |
13 | nor should it be construed as a style guide. |
a0d0e21e |
14 | |
15 | The Perl motto still holds: There's more than one way to do it. |
16 | |
c2960299 |
17 | =head1 OO SCALING TIPS |
18 | |
19 | =over 5 |
20 | |
21 | =item 1 |
22 | |
23 | Do not attempt to verify the type of $self. That'll break if the class is |
24 | inherited, when the type of $self is valid but its package isn't what you |
25 | expect. See rule 5. |
26 | |
27 | =item 2 |
28 | |
29 | If an object-oriented (OO) or indirect-object (IO) syntax was used, then the |
30 | object is probably the correct type and there's no need to become paranoid |
31 | about it. Perl isn't a paranoid language anyway. If people subvert the OO |
32 | or IO syntax then they probably know what they're doing and you should let |
33 | them do it. See rule 1. |
34 | |
35 | =item 3 |
36 | |
37 | Use the two-argument form of bless(). Let a subclass use your constructor. |
38 | See L<INHERITING A CONSTRUCTOR>. |
39 | |
40 | =item 4 |
41 | |
42 | The subclass is allowed to know things about its immediate superclass, the |
43 | superclass is allowed to know nothing about a subclass. |
44 | |
45 | =item 5 |
46 | |
47 | Don't be trigger happy with inheritance. A "using", "containing", or |
48 | "delegation" relationship (some sort of aggregation, at least) is often more |
49 | appropriate. See L<OBJECT RELATIONSHIPS>, L<USING RELATIONSHIP WITH SDBM>, |
50 | and L<"DELEGATION">. |
51 | |
52 | =item 6 |
53 | |
54 | The object is the namespace. Make package globals accessible via the |
55 | object. This will remove the guess work about the symbol's home package. |
56 | See L<CLASS CONTEXT AND THE OBJECT>. |
57 | |
58 | =item 7 |
59 | |
5f05dabc |
60 | IO syntax is certainly less noisy, but it is also prone to ambiguities that |
c2960299 |
61 | can cause difficult-to-find bugs. Allow people to use the sure-thing OO |
62 | syntax, even if you don't like it. |
63 | |
64 | =item 8 |
65 | |
66 | Do not use function-call syntax on a method. You're going to be bitten |
67 | someday. Someone might move that method into a superclass and your code |
68 | will be broken. On top of that you're feeding the paranoia in rule 2. |
69 | |
70 | =item 9 |
71 | |
72 | Don't assume you know the home package of a method. You're making it |
73 | difficult for someone to override that method. See L<THINKING OF CODE REUSE>. |
74 | |
75 | =back |
76 | |
a0d0e21e |
77 | =head1 INSTANCE VARIABLES |
78 | |
79 | An anonymous array or anonymous hash can be used to hold instance |
80 | variables. Named parameters are also demonstrated. |
81 | |
82 | package Foo; |
83 | |
84 | sub new { |
769c2898 |
85 | my $type = shift; |
a0d0e21e |
86 | my %params = @_; |
769c2898 |
87 | my $self = {}; |
88 | |
89 | $self->{High} = $params{High}; |
90 | $self->{Low} = $params{Low}; |
91 | |
c2960299 |
92 | bless $self, $type; |
a0d0e21e |
93 | } |
94 | |
95 | |
96 | package Bar; |
97 | |
98 | sub new { |
769c2898 |
99 | my $type = shift; |
a0d0e21e |
100 | my %params = @_; |
769c2898 |
101 | my $self = []; |
102 | |
103 | $self->[0] = $params{Left}; |
104 | $self->[1] = $params{Right}; |
105 | |
c2960299 |
106 | bless $self, $type; |
a0d0e21e |
107 | } |
108 | |
109 | package main; |
110 | |
769c2898 |
111 | my $a = Foo->new( High => 42, Low => 11 ); |
112 | print "High = $a->{High}\n"; |
113 | print "Low = $a->{Low}\n"; |
a0d0e21e |
114 | |
769c2898 |
115 | my $b = Bar->new( Left => 78, Right => 40 ); |
116 | print "Left = $b->[0]\n"; |
117 | print "Right = $b->[1]\n"; |
a0d0e21e |
118 | |
a0d0e21e |
119 | =head1 SCALAR INSTANCE VARIABLES |
120 | |
121 | An anonymous scalar can be used when only one instance variable is needed. |
122 | |
123 | package Foo; |
124 | |
125 | sub new { |
126 | my $type = shift; |
769c2898 |
127 | my $self = shift; |
128 | |
c2960299 |
129 | bless \$self, $type; |
a0d0e21e |
130 | } |
131 | |
132 | package main; |
133 | |
769c2898 |
134 | my $a = Foo->new( 42 ); |
135 | print "a = $$a\n"; |
a0d0e21e |
136 | |
137 | |
138 | =head1 INSTANCE VARIABLE INHERITANCE |
139 | |
140 | This example demonstrates how one might inherit instance variables from a |
141 | superclass for inclusion in the new class. This requires calling the |
142 | superclass's constructor and adding one's own instance variables to the new |
143 | object. |
144 | |
145 | package Bar; |
146 | |
147 | sub new { |
c2960299 |
148 | my $type = shift; |
a0d0e21e |
149 | my $self = {}; |
769c2898 |
150 | |
151 | $self->{buz} = 42; |
152 | |
c2960299 |
153 | bless $self, $type; |
a0d0e21e |
154 | } |
155 | |
156 | package Foo; |
769c2898 |
157 | our @ISA = qw( Bar ); |
a0d0e21e |
158 | |
159 | sub new { |
c2960299 |
160 | my $type = shift; |
161 | my $self = Bar->new; |
769c2898 |
162 | |
163 | $self->{biz} = 11; |
164 | |
c2960299 |
165 | bless $self, $type; |
a0d0e21e |
166 | } |
167 | |
168 | package main; |
169 | |
769c2898 |
170 | my $a = Foo->new; |
171 | print "buz = $a->{buz}\n"; |
172 | print "biz = $a->{biz}\n"; |
a0d0e21e |
173 | |
174 | |
175 | |
176 | =head1 OBJECT RELATIONSHIPS |
177 | |
178 | The following demonstrates how one might implement "containing" and "using" |
179 | relationships between objects. |
180 | |
181 | package Bar; |
182 | |
183 | sub new { |
c2960299 |
184 | my $type = shift; |
a0d0e21e |
185 | my $self = {}; |
769c2898 |
186 | |
187 | $self->{buz} = 42; |
188 | |
c2960299 |
189 | bless $self, $type; |
a0d0e21e |
190 | } |
191 | |
192 | package Foo; |
193 | |
194 | sub new { |
c2960299 |
195 | my $type = shift; |
a0d0e21e |
196 | my $self = {}; |
769c2898 |
197 | |
198 | $self->{Bar} = Bar->new; |
199 | $self->{biz} = 11; |
200 | |
c2960299 |
201 | bless $self, $type; |
a0d0e21e |
202 | } |
203 | |
204 | package main; |
205 | |
769c2898 |
206 | my $a = Foo->new; |
207 | print "buz = $a->{Bar}->{buz}\n"; |
208 | print "biz = $a->{biz}\n"; |
a0d0e21e |
209 | |
210 | |
211 | |
212 | =head1 OVERRIDING SUPERCLASS METHODS |
213 | |
4633a7c4 |
214 | The following example demonstrates how to override a superclass method and |
215 | then call the overridden method. The B<SUPER> pseudo-class allows the |
216 | programmer to call an overridden superclass method without actually knowing |
217 | where that method is defined. |
a0d0e21e |
218 | |
219 | package Buz; |
220 | sub goo { print "here's the goo\n" } |
221 | |
769c2898 |
222 | |
223 | package Bar; |
224 | our @ISA = qw( Buz ); |
a0d0e21e |
225 | sub google { print "google here\n" } |
226 | |
769c2898 |
227 | |
a0d0e21e |
228 | package Baz; |
229 | sub mumble { print "mumbling\n" } |
230 | |
231 | package Foo; |
769c2898 |
232 | our @ISA = qw( Bar Baz ); |
a0d0e21e |
233 | |
c2960299 |
234 | sub new { |
235 | my $type = shift; |
236 | bless [], $type; |
237 | } |
a0d0e21e |
238 | sub grr { print "grumble\n" } |
239 | sub goo { |
240 | my $self = shift; |
4633a7c4 |
241 | $self->SUPER::goo(); |
a0d0e21e |
242 | } |
243 | sub mumble { |
244 | my $self = shift; |
4633a7c4 |
245 | $self->SUPER::mumble(); |
a0d0e21e |
246 | } |
247 | sub google { |
248 | my $self = shift; |
4633a7c4 |
249 | $self->SUPER::google(); |
a0d0e21e |
250 | } |
251 | |
252 | package main; |
253 | |
769c2898 |
254 | my $foo = Foo->new; |
a0d0e21e |
255 | $foo->mumble; |
256 | $foo->grr; |
257 | $foo->goo; |
258 | $foo->google; |
259 | |
260 | |
c2960299 |
261 | =head1 USING RELATIONSHIP WITH SDBM |
a0d0e21e |
262 | |
263 | This example demonstrates an interface for the SDBM class. This creates a |
264 | "using" relationship between the SDBM class and the new class Mydbm. |
265 | |
a0d0e21e |
266 | package Mydbm; |
267 | |
769c2898 |
268 | use SDBM_File; |
269 | use Tie::Hash; |
270 | |
271 | our @ISA = qw( Tie::Hash ); |
c2960299 |
272 | |
a0d0e21e |
273 | sub TIEHASH { |
c2960299 |
274 | my $type = shift; |
a0d0e21e |
275 | my $ref = SDBM_File->new(@_); |
769c2898 |
276 | bless { dbm => $ref }, $type; |
a0d0e21e |
277 | } |
769c2898 |
278 | |
a0d0e21e |
279 | sub FETCH { |
280 | my $self = shift; |
769c2898 |
281 | my $ref = $self->{dbm}; |
a0d0e21e |
282 | $ref->FETCH(@_); |
283 | } |
769c2898 |
284 | |
a0d0e21e |
285 | sub STORE { |
54310121 |
286 | my $self = shift; |
769c2898 |
287 | |
288 | if ( defined $_[0] ) { |
289 | my $ref = $self->{dbm}; |
a0d0e21e |
290 | $ref->STORE(@_); |
291 | } else { |
292 | die "Cannot STORE an undefined key in Mydbm\n"; |
293 | } |
294 | } |
295 | |
296 | package main; |
c2960299 |
297 | use Fcntl qw( O_RDWR O_CREAT ); |
a0d0e21e |
298 | |
769c2898 |
299 | tie my %foo, 'Mydbm', 'Sdbm', O_RDWR|O_CREAT, 0640; |
300 | $foo{bar} = 123; |
301 | print "foo-bar = $foo{bar}\n"; |
a0d0e21e |
302 | |
769c2898 |
303 | tie my %bar, 'Mydbm', 'Sdbm2', O_RDWR|O_CREAT, 0640; |
304 | $bar{Cathy} = 456; |
305 | print "bar-Cathy = $bar{Cathy}\n"; |
a0d0e21e |
306 | |
307 | =head1 THINKING OF CODE REUSE |
308 | |
309 | One strength of Object-Oriented languages is the ease with which old code |
310 | can use new code. The following examples will demonstrate first how one can |
311 | hinder code reuse and then how one can promote code reuse. |
312 | |
313 | This first example illustrates a class which uses a fully-qualified method |
314 | call to access the "private" method BAZ(). The second example will show |
315 | that it is impossible to override the BAZ() method. |
316 | |
317 | package FOO; |
318 | |
c2960299 |
319 | sub new { |
320 | my $type = shift; |
321 | bless {}, $type; |
322 | } |
769c2898 |
323 | |
a0d0e21e |
324 | sub bar { |
325 | my $self = shift; |
326 | $self->FOO::private::BAZ; |
327 | } |
328 | |
329 | package FOO::private; |
330 | |
331 | sub BAZ { |
332 | print "in BAZ\n"; |
333 | } |
334 | |
335 | package main; |
336 | |
769c2898 |
337 | my $a = FOO->new; |
a0d0e21e |
338 | $a->bar; |
339 | |
340 | Now we try to override the BAZ() method. We would like FOO::bar() to call |
d1b91892 |
341 | GOOP::BAZ(), but this cannot happen because FOO::bar() explicitly calls |
a0d0e21e |
342 | FOO::private::BAZ(). |
343 | |
344 | package FOO; |
345 | |
c2960299 |
346 | sub new { |
347 | my $type = shift; |
348 | bless {}, $type; |
349 | } |
769c2898 |
350 | |
a0d0e21e |
351 | sub bar { |
352 | my $self = shift; |
353 | $self->FOO::private::BAZ; |
354 | } |
355 | |
356 | package FOO::private; |
357 | |
358 | sub BAZ { |
359 | print "in BAZ\n"; |
360 | } |
361 | |
362 | package GOOP; |
769c2898 |
363 | |
364 | our @ISA = qw( FOO ); |
365 | |
c2960299 |
366 | sub new { |
367 | my $type = shift; |
368 | bless {}, $type; |
369 | } |
a0d0e21e |
370 | |
371 | sub BAZ { |
372 | print "in GOOP::BAZ\n"; |
373 | } |
374 | |
375 | package main; |
376 | |
769c2898 |
377 | my $a = GOOP->new; |
a0d0e21e |
378 | $a->bar; |
379 | |
380 | To create reusable code we must modify class FOO, flattening class |
381 | FOO::private. The next example shows a reusable class FOO which allows the |
382 | method GOOP::BAZ() to be used in place of FOO::BAZ(). |
383 | |
384 | package FOO; |
385 | |
c2960299 |
386 | sub new { |
387 | my $type = shift; |
388 | bless {}, $type; |
389 | } |
769c2898 |
390 | |
a0d0e21e |
391 | sub bar { |
392 | my $self = shift; |
393 | $self->BAZ; |
394 | } |
395 | |
396 | sub BAZ { |
397 | print "in BAZ\n"; |
398 | } |
399 | |
400 | package GOOP; |
769c2898 |
401 | |
402 | our @ISA = qw( FOO ); |
a0d0e21e |
403 | |
c2960299 |
404 | sub new { |
405 | my $type = shift; |
406 | bless {}, $type; |
407 | } |
769c2898 |
408 | |
a0d0e21e |
409 | sub BAZ { |
410 | print "in GOOP::BAZ\n"; |
411 | } |
412 | |
413 | package main; |
414 | |
769c2898 |
415 | my $a = GOOP->new; |
a0d0e21e |
416 | $a->bar; |
417 | |
418 | =head1 CLASS CONTEXT AND THE OBJECT |
419 | |
420 | Use the object to solve package and class context problems. Everything a |
421 | method needs should be available via the object or should be passed as a |
422 | parameter to the method. |
423 | |
424 | A class will sometimes have static or global data to be used by the |
425 | methods. A subclass may want to override that data and replace it with new |
426 | data. When this happens the superclass may not know how to find the new |
427 | copy of the data. |
428 | |
429 | This problem can be solved by using the object to define the context of the |
430 | method. Let the method look in the object for a reference to the data. The |
431 | alternative is to force the method to go hunting for the data ("Is it in my |
432 | class, or in a subclass? Which subclass?"), and this can be inconvenient |
5f05dabc |
433 | and will lead to hackery. It is better just to let the object tell the |
a0d0e21e |
434 | method where that data is located. |
435 | |
436 | package Bar; |
437 | |
769c2898 |
438 | my %fizzle = ( Password => 'XYZZY' ); |
a0d0e21e |
439 | |
440 | sub new { |
c2960299 |
441 | my $type = shift; |
a0d0e21e |
442 | my $self = {}; |
769c2898 |
443 | $self->{fizzle} = \%fizzle; |
c2960299 |
444 | bless $self, $type; |
a0d0e21e |
445 | } |
446 | |
447 | sub enter { |
448 | my $self = shift; |
54310121 |
449 | |
a0d0e21e |
450 | # Don't try to guess if we should use %Bar::fizzle |
451 | # or %Foo::fizzle. The object already knows which |
452 | # we should use, so just ask it. |
453 | # |
769c2898 |
454 | my $fizzle = $self->{fizzle}; |
a0d0e21e |
455 | |
769c2898 |
456 | print "The word is $fizzle->{Password}\n"; |
a0d0e21e |
457 | } |
458 | |
459 | package Foo; |
a0d0e21e |
460 | |
769c2898 |
461 | our @ISA = qw( Bar ); |
462 | |
463 | my %fizzle = ( Password => 'Rumple' ); |
a0d0e21e |
464 | |
465 | sub new { |
c2960299 |
466 | my $type = shift; |
a0d0e21e |
467 | my $self = Bar->new; |
769c2898 |
468 | $self->{fizzle} = \%fizzle; |
c2960299 |
469 | bless $self, $type; |
a0d0e21e |
470 | } |
471 | |
472 | package main; |
473 | |
769c2898 |
474 | my $a = Bar->new; |
475 | my $b = Foo->new; |
476 | |
a0d0e21e |
477 | $a->enter; |
478 | $b->enter; |
479 | |
d1b91892 |
480 | =head1 INHERITING A CONSTRUCTOR |
481 | |
482 | An inheritable constructor should use the second form of bless() which allows |
483 | blessing directly into a specified class. Notice in this example that the |
484 | object will be a BAR not a FOO, even though the constructor is in class FOO. |
485 | |
486 | package FOO; |
487 | |
488 | sub new { |
489 | my $type = shift; |
490 | my $self = {}; |
491 | bless $self, $type; |
492 | } |
493 | |
494 | sub baz { |
495 | print "in FOO::baz()\n"; |
496 | } |
497 | |
498 | package BAR; |
769c2898 |
499 | |
500 | our @ISA = qw(FOO); |
d1b91892 |
501 | |
502 | sub baz { |
503 | print "in BAR::baz()\n"; |
504 | } |
505 | |
506 | package main; |
507 | |
769c2898 |
508 | my $a = BAR->new; |
d1b91892 |
509 | $a->baz; |
510 | |
511 | =head1 DELEGATION |
512 | |
513 | Some classes, such as SDBM_File, cannot be effectively subclassed because |
514 | they create foreign objects. Such a class can be extended with some sort of |
515 | aggregation technique such as the "using" relationship mentioned earlier or |
516 | by delegation. |
517 | |
518 | The following example demonstrates delegation using an AUTOLOAD() function to |
519 | perform message-forwarding. This will allow the Mydbm object to behave |
520 | exactly like an SDBM_File object. The Mydbm class could now extend the |
521 | behavior by adding custom FETCH() and STORE() methods, if this is desired. |
522 | |
523 | package Mydbm; |
524 | |
769c2898 |
525 | use SDBM_File; |
526 | use Tie::Hash; |
527 | |
528 | our @ISA = qw( Tie::Hash ); |
529 | our $AUTOLOAD; |
d1b91892 |
530 | |
531 | sub TIEHASH { |
532 | my $type = shift; |
769c2898 |
533 | my $ref = SDBM_File->new(@_); |
534 | bless { delegate => $ref }; |
d1b91892 |
535 | } |
536 | |
537 | sub AUTOLOAD { |
538 | my $self = shift; |
539 | |
540 | # The Perl interpreter places the name of the |
541 | # message in a variable called $AUTOLOAD. |
542 | |
543 | # DESTROY messages should never be propagated. |
544 | return if $AUTOLOAD =~ /::DESTROY$/; |
545 | |
546 | # Remove the package name. |
547 | $AUTOLOAD =~ s/^Mydbm:://; |
548 | |
549 | # Pass the message to the delegate. |
769c2898 |
550 | $self->{delegate}->$AUTOLOAD(@_); |
d1b91892 |
551 | } |
552 | |
553 | package main; |
554 | use Fcntl qw( O_RDWR O_CREAT ); |
555 | |
769c2898 |
556 | tie my %foo, 'Mydbm', 'adbm', O_RDWR|O_CREAT, 0640; |
557 | $foo{bar} = 123; |
558 | print "foo-bar = $foo{bar}\n"; |