Beefy Boxes and Bandwidth Generously Provided by pair Networks
Don't ask to ask, just ask
 
PerlMonks  

Polymorphism and Abstraction

by hypochrismutreefuzz (Scribe)
on Aug 15, 2004 at 16:49 UTC ( [id://383108]=perlmeditation: print w/replies, xml ) Need Help??

I was thinking in regards to It's a dog, but what kind? (polymorphism , in Perl OO), "Its a dog, but what kind". Polymorphism in Perl seems to me to be largely a matter of style. For instance, in C++, you can have an abstract base class that defines virtual functions that are overridden by the derived classes. You have something like:

CBase *p; p = new(CDerived); p->virtual_function();

The C++ compiler and linker handle all the chores of setting up the vtable and dispatching function calls to the correct function definition. The system provides a built in layer of abstraction between the idea of the object and its implimentation. It occured to me that a proxy class might provide a similar abstraction for Perl. Continuing the previous posters use of dogs:

package Dog::Proxy; use strict; use Dog::Base; use Dog::Cocker; use Dog::CattleDog; my %derived_classes = ( cocker => 'Dog::Cocker', cattledog => 'Dog::CattleDog', ); sub new { my $class = shift; die if ref $class; my $type = shift; die "Cant create Dog::Base without breed" unless $type; die "Type $type not registered" unless exists $derived_classes{$ty +pe}; my $class = $derived_classes{$type}; return $class->new(); } 1;

The Dog::Proxy class holds all the information about the Dog hierarchy and all construction of objects deriving from Dog::Base take place here. The proxy has no methods or attributes and passes back a reference to the new Dog::Breed. I also disallowed the creation of a Dog::Base, but I suppose that in practice it might be preferable to be able to create a base class object as well.

package Dog::Base; use strict; my %basic_attr = ( legs=>4, tail=>1, nose=>'wet', ); sub new { my $class = shift; my $self = {} unless ref $class; $self = { base=>{%basic_attr}, breed=>{}, }; return bless($self, $class); } 1;

Dog::Base currently just initializes some attributes that supposedly will be common to all Dogs.

package Dog::Cocker; use strict; require Dog::Base; use vars '@ISA'; @ISA = 'Dog::Base'; my %cocker_attr = ( habits => 'barks at strangers', size => 'small', temperment => 'very loyal' ); sub new { my $class = shift; my $self = Dog::Base->new() unless ref $class; $self->{breed} = {%cocker_attr}; return bless($self, $class); } sub bark { my $self = shift; print "yarf\n"; } sub wag_tail { my $self = shift; print "tail wagging\n"; } 1; package Dog::CattleDog; use strict; require Dog::Base; use vars '@ISA'; @ISA = 'Dog::Base'; my %cattledog_attr = ( temperment => 'fiercley loyal', habits => 'leary of strangers', size => 'medium', ); sub new { my $class = shift; my $self = Dog::Base->new() unless ref $class; $self->{breed} = {%cattledog_attr}; return bless($self, $class); } sub bark { my $self = shift; print "shriek\n"; } sub wag_tail { my $self = shift; print "tail over back\n"; } 1;

And the test code:

#!/usr/local/bin/perl -wT # test.pl use Dog::Proxy; use Data::Dumper; use strict; my $cosette= Dog::Proxy->new('cattledog'); my $moose= Dog::Proxy->new('cocker'); print Dumper($cosette,$moose); $cosette->bark; $cosette->wag_tail; $moose->bark; $moose->wag_tail;

With the results:

$VAR1 = bless( { 'breed' => { 'habits' => 'barks at strangers', 'temperment' => 'very loyal', 'size' => 'small' }, 'base' => { 'legs' => 4, 'nose' => 'wet', 'tail' => 1 } }, 'Dog::Cocker' ); $VAR1 = bless( { 'breed' => { 'habits' => 'leary of strangers', 'temperment' => 'fiercley loyal', 'size' => 'medium' }, 'base' => { 'legs' => 4, 'nose' => 'wet', 'tail' => 1 } }, 'Dog::CattleDog' ); yarf tail wagging shriek tail over back

A somewhat simple if not simplistic hierarchy. The proxy provides a benifit in that it abstracts out the concepts implicit in constructing an instance of a more abstract class, in this case a type of dog.

In my own development I have been attempting to go from the specific to the general in this way; by abstracting out the common elements of each type of thing, and by trying to notice what patterns tend to be used by each type. That is, what is the same about each type and what is different.

Replies are listed 'Best First'.
Re: Polymorphism and Abstraction
by exussum0 (Vicar) on Aug 15, 2004 at 17:48 UTC
    What you have done isn't a Proxy per se. You have just created an object factory.

    What may be wiser is to eliminate the "proxy" and pass breeds into the type Dog. Create a common class called Breed, and a second one extending Breed called DogBreed.

    If you are worried about someday of an impossible breed being assigned to a wrong animal, pass the animal to the breed extensions (DogBreed,CatBreed) to validate that what you are about to assign is compatable. Or you can have the base breed provide a function to Dog for it to call to validate you aren't doing that type of silliness.

    But don't get me wrong, object factories are really useful for uncanny patterns of construction. KatterBox does this, as every katterbox needs a configuration based on an input parameter. The factory method will sort everything out and pass back a katterbox object.

    The proxy pattern, aka delegation pattern, is useful too. They are great for when you want to leave the flow of calls alone, but inserting a behaviour inbetween for some reason. In things like DB calls, a proxy pattern could help you find out how long a call took and how many times it was called.

    Nice work none-the-less. I'm sure if you think in terms of 1000 breeds, and maybe expanding out your pattern to various species, you'll change your design. For a very small set of objects, your design is quite sound.

    ----
    Then B.I. said, "Hov' remind yourself nobody built like you, you designed yourself"

      Thanks for the good words. I have been turning this notion into a real live program recently.

      I am a little vague about all the various patterns, I suppose.

      In the real program I just pass all the object related information directly into the constructor. Perhaps I don't strictly speaking need the intermediary of the object factory but it allows me to split the code between two modules which helps to organize my thoughts better.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlmeditation [id://383108]
Approved by davido
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others lurking in the Monastery: (5)
As of 2024-04-24 03:54 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found