http://qs321.pair.com?node_id=336677

dada has asked for the wisdom of the Perl Monks concerning the following question:

hello,

I have a little doubt about OO terminology. we all know that we can do:

package Foo; sub bar { print "Foo::bar!\n"; } package main; my $x = 'Foo'; $x->bar();
and we known that bar(), in this case, is being called as a "class method" (or "static method") instead of as an "instance method".

but how do you call $x? it is just a string, which represents a package name. so it doesn't seems a "class reference". maybe a "symbolic class reference"?

I came across this trying to implement an object hierarchy in which I can have several "backend" classes and a "frontend" object. now, I don't need to instantiate backend objects, I only need to know which backend the main object refers to. so I thought something like this:

package Thing; sub new { my($class, %option) = @_; my $self = { backend => "Thing::Backend::".$option{backend}, }; return bless $self, $class; } sub backend { my($self) = @_; return $self->{backend}; } package Thing::Backend::A; sub do { print "doing A\n"; } package Thing::Backend::B; sub do { print "doing B\n"; } package Thing::Backend::C; sub do { print "doing C\n"; } package main; my $x = Thing->new( backend => 'A' ); $x->backend->do();
does this makes some sense? are there better ways, in OO practice, to implement such a structure?

cheers,
Aldo

King of Laziness, Wizard of Impatience, Lord of Hubris

Replies are listed 'Best First'.
Re: how do you call this? $x->bar();
by Corion (Patriarch) on Mar 15, 2004 at 12:05 UTC

    In Perl, there is no difference between the package of a class and the name of the class. I would simply call $x the class name or short the class.

Re: how do you call this? $x->bar();
by Aristotle (Chancellor) on Mar 15, 2004 at 12:11 UTC
    It's just the class name.

    Makeshifts last the longest.

Re: how do you call this? $x->bar();
by rinceWind (Monsignor) on Mar 15, 2004 at 14:55 UTC
    So, in a nutshell, your objects of class "Thing" contain the class name of the back end class, in the hash member $x->{backend}. As far as I am concerned, this is perfectly acceptable.

    There may be alternatives, if for instance you actually need to have (and keep track of) an object blessed into the back end class, associated with the "Thing" object. In this case, you would just store a reference to this object (you can then get its class name using ref if you really want it).

    Another alternative is the factory pattern, where method calls in your "Thing" class spit out backend objects given parameters, etc.

    It's a case of TIMTOWDI.

    --
    I'm Not Just Another Perl Hacker

      Another alternative is the factory pattern, where method calls in your "Thing" class spit out backend objects given parameters, etc.
      It seems to me that a factory pattern is exactly what he is doing here.
      package main; my $x = Thing->new( backend => 'A' ); $x->backend->do();
      Of course the way he is doing it is not the way the Gang of Four show in the Design Patterns book, but he's not coding it in C++ or smalltalk (which they do).

      Much as you say, TIMTOWTDI.

      -stvn
        It seems to me that a factory pattern is exactly what he is doing here.

        Not to quibble, but no, I don't think that's what "factory pattern" means.

        A factory hides the mechanics of constructing new objects behind another interface, whereas all of the instantiation done in this example is explicit.

        I'd say the OP is showing a behavior composition technique, like the GoF's Strategy pattern, expressed in proper Perl idiom.

Re: how do you call this? $x->bar();
by NetWallah (Canon) on Mar 15, 2004 at 19:10 UTC
    Couldn't this be done using an Interface inheritence paradigm ?

    I havn't done much of this in perl, so I wont provide potentially misleading code, but the op's code seems to me like a simple overriding of an inherited method.

    Offense, like beauty, is in the eye of the beholder, and a fantasy.
    By guaranteeing freedom of expression, the First Amendment also guarntees offense.
      Couldn't this be done using an Interface inheritence paradigm ? This course would be consistent with the OP expressed intent, because Thing::Backend::A isn't much different than my $obj = Thing->new(backend => 'A'); $obj->backend->do(); In this case Interface inheritance is actually more straight forward and more readable. However, I think that the OP's implied intent was to abstract the decision of which backend object to use from the user. If I were writing this it would of course use interface inheritance so that all the backend objects looked the same. I am personally more fond of the factory pattern, coming from java, however the composition pattern works just as well. For example:
      package Thing; # default constructor for all "Thing"s sub new { my ($class,%options) = @_; my $self = { %options }; return bless $self, ref($class) || $class; } # abstract method stub sub do { my $self = shift; die ref($self) || $self . " did not implement do()\n"; } package Thing::A sub do { my $self = shift; print "This is " . ref($self) || $self . "\n"; } package Thing::B sub do { my $self = shift; print "I'm a really cool " . ref($self) || $self . "\n"; } package ThingFactory; my %dispatch = ( 'A' => 'Thing::A', 'B' => 'Thing::B', '_default' => 'Thing::A', ); sub createThing { my ($class,%options) = @_; unless( exists($options{'thing'}) && $options{'thing'} ) { return $dispatch{'_default'}->new(%options); } elsif( !exists($dispatch{$options{'thing'}}) ) { return $dispatch{'_default'}->new(%options); } else { return $dispatch{$options{'thing'}}->new(%options); } } package main; my $thing = ThingFactory->createThing('thing' => 'A'); $thing->do();
      (Note this code is untested, but the the premise is sound, i may have missed a couple of parenthesis or curly braces)

      Something else to note: ThingFactory::createThing's implementation is very simple, of course this will be as simple or complex as you need. Think about instead of the user deciding what thing he wants, having the user pass in some other info maybe some attributes of the thing they are looking for, then createThing becomes much more useful because you can make it know more about the implementations of Thing and not require the user code to worry about searching the implementations for what they want. This is the beauty of the Factory pattern.

      Of course you should substitute createThing with your own logic for creating a Thing class but this was an example (I'm also a fan of dispatch tables =] ), a possibly more efficient way other than a dispatch table is to surround the constructor call in an eval such as my $thing = eval { "Thing::$options{'thing'}"->new(%options); };(again untested but it's the concept that counts) You do need the eval so it won't throw you program into fits if you don't have a Thing::C. Also the default is not a defacto part of the pattern, but for situations where it is appropriate I like to add that. Of course with perl TIMTOWTDI, however, I like to stick as closely to common OO standards (by that I mean what you mainly find in Java and other "pure" OO languages) as possible, even when perl affords me the opportunity to cheat (which it does a lot). That's a personal preference of mine, most people think i'm crazy for it. Ah, to each his own. Well, this post turned out to be a stream of conscousness(sp). Do with it what you will.

        Beautiful job, linux454 - sorry I cant ++ you more than once!

        Offense, like beauty, is in the eye of the beholder, and a fantasy.
        By guaranteeing freedom of expression, the First Amendment also guarntees offense.