Beefy Boxes and Bandwidth Generously Provided by pair Networks
There's more than one way to do things
 
PerlMonks  

Decorator(?) classes and inheritance

by dragonchild (Archbishop)
on Jan 30, 2004 at 13:29 UTC ( [id://325222]=perlquestion: print w/replies, xml ) Need Help??

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

Update2: For those who still don't get it, here's the gist:

The actual problem is I have a baseclass A and two children B and C, each overriding a different method in A. How do I have D inherit from B and C and have both overriden methods without having D know about the innards of A, B, and C?

I think these are Decorator classes. I've got a hand-rolled OO representation baseclass (similar to Class::MethodMaker, but optimized for our application) that creates mutators given a set of attributes. I've also got two different decorators on that. One is a Singleton baseclass that only overrides new(). The other is a ReadOnly baseclass that overrides make_attr_subs(). The issue I'm having is how to make a readonly singleton, using this system.

If I try to inherit from both classes, I'm only going to get the behavior of the first class in @ISA, because both decorators inherit from the baseclass.

I thought about modifying the child class using import(). I can't see any gaping holes in that theory, but I'm loath to use import() in an OO hierarchy. What do others think?

Update: I don't think it was buzzword bingo, but here's some pseudocode.

package Base; sub new { # Creates a new instance of the class } sub attrs { # When called as a class method, will keep track of attribute name +s } sub make_attr_subs { # Called by attrs() to create the mutators } -------------------- package Singleton; sub new { # Instead of creating a new instance, will return an already creat +ed one. } -------------------- package ReadOnly; sub make_attr_subs { # Instead of creating just a mutator, will create an accessor X() +and a mutator _X(). }

Obviously, there's more to the classes than just those methods, including a lot of bookkeeping. But, those are the relevant parts for this discussion. (I also thought that was clear from the text, but maybe not.)

------
We are the carpenters and bricklayers of the Information Age.

Please remember that I'm crufty and crochety. All opinions are purely mine and all code is untested, unless otherwise specified.

Replies are listed 'Best First'.
Re: Decorator(?) classes and inheritance
by halley (Prior) on Jan 30, 2004 at 14:43 UTC

    I don't have the gang of four here at my cubicle, but I do develop code which uses decorators.

    Decorator B should not "inherit" anything from A; they are more of a 'friend' relationship. In Perl, you would probably end up munging the symbol table to accomplish it. Decorators are added after-the-fact, per-instance, upon demand. So SOME instances of A also have B-like qualities, and SOME instances of A do not.

    Another way of saying it: You don't really say "make a new instance of a B," but instead, "make a new instance of an A, and then attach some B-ness to it."

    my $a = new A(); $a->bee(); # A::bee() does not exist or has no effect $a->attach('B'); # make A load B and attach B methods $a->bee(); # A::bee() now exists thanks to B::bee

    This approach would still hold true, even if you make another Decorator like C. Some A instances may have B, some have C, some have both B and C, and some have neither.

    Update: You can then make a D class which inherits from A, but always attaches B-ness and C-ness.

    --
    [ e d @ h a l l e y . c c ]

      Decorator B should not "inherit" anything from A; they are more of a 'friend' relationship.

      Actually, it's common for decorator classes to inherit from the base class that they decorate. That way they get the same interface, isa works, etc. The examples in the GOF book do exactly this.

Re: Decorator(?) classes and inheritance
by adrianh (Chancellor) on Jan 30, 2004 at 15:53 UTC

    Another approach would be to treat ReadOnly and Singleton more like mixins, don't have them inherit from Base, and use NEXT something like this:

    package Base; sub new { my $class = shift; bless { @_ }, $class; }; sub make_attr_subs { my $self = shift; $self->{Base_called} = 1; }; package Singleton; my %Singleton; sub new { my $class = shift; return $Singleton{$class} ||= $class->NEXT::new(@_); }; package ReadOnly; use NEXT; sub make_attr_subs { my $self = shift; $self->{ReadOnly_called} = 1; $self->NEXT::make_attr_subs; }; package ReadOnlySingleton; use base qw(Singleton ReadOnly Base); package main; use Test::More 'no_plan'; sub new_object { ReadOnlySingleton->new }; isa_ok new_object(), 'ReadOnlySingleton'; is new_object(), new_object(), 'object is a singleton'; my $o = new_object(); $o->make_attr_subs; ok $o->{Base_called}, 'make_attr_subs in Base called'; ok $o->{ReadOnly_called}, 'make_attr_subs in ReadOnly called';
Re: Decorator(?) classes and inheritance
by TravelByRoad (Acolyte) on Jan 30, 2004 at 15:00 UTC
    This seems too easy to be right, but I thought I would throw it out here for my edification if not dragonchild's. How about
    package D; sub new { Singleton::new( @_ ) }; sub make_attr_subs { ReadOnly::make_attr_subs( @_ ) };
    How does this miss the mark in providing the D behavior you wish?

    David

      That does do what I want, program-wise. The issue is that I don't want D to know where Singleton and ReadOnly do their work. As far as D should be concerned, Singleton and ReadOnly automagically do the right thing. For example, make_attr_subs() is a function that the clients of this object family should never know about. Additionally, new() is a function that that the clients are expressly forbidden to override (or the OO contract is violated).

      ------
      We are the carpenters and bricklayers of the Information Age.

      Please remember that I'm crufty and crochety. All opinions are purely mine and all code is untested, unless otherwise specified.

        I can see why you dont wan't D to understand where ReadOnly does its work, that makes sense, ReadOnly should just shadow the Base class. But I do not understand why you care about D knowing where Singleton does its work. It seems to me that if you are going to create a Singleton, it is obvious that it will do its work at creation time, and so therefore you would need to call Singleton::new().

        I think maybe you are going to far with trying to decouple your class's knowledge of one another. There comes a point in all designs, when you have to say enough is enough. If you have a really compeling reason, let us hear it, otherwise, I would dispatch to Singleton::new() explictly and make that a condition of using the Singleton class.

        One things too that I was unclear on, is if your Singleton class inherits from Base. Your pseudo-code would indicate not, but some of what you are saying makes me think they are supposed to. If that is the case (Singleton is-a Base), then I would recommend a re-thinking of that.

        To be honest though, without seeing alot more code, I can do nothing but speculate here. I have a class framework that I have been working on for the last 2 years, and addresses problems like this very simply by using (Trait/mix-in)-like (pseudo-incomplete) objects, and always seperating the creation of my objects (the new() method) from the initialization (the _init() method in my framework). I also take advantage of the perl symbol table's "hackability" to do Eiffel-like renaming and dispatching of methods, you can check my scratchpad to see a quick example of that type of code (and a more detailed explaination of it in this node for which the scratchpad was created).

        -stvn
Re: Decorator(?) classes and inheritance
by adrianh (Chancellor) on Jan 30, 2004 at 15:08 UTC
    I think these are Decorator classes.

    In the design pattern world decorator classes are run-time things, rather than behaviour added by inheritance. You wrap your object inside a decorator class object which then delegates and adds behaviour as appropriate.

    You might want to consider this approach as an alternative to inheritance.

Re: Decorator(?) classes and inheritance
by halley (Prior) on Jan 30, 2004 at 13:56 UTC
    I think these are Decorator classes.
    Syntax error in sentence one. Unbound pronoun 'these'.
    Can you show us some example code, even just pseudocode that sketches out your concept? The question as it stands is just buzzword bingo. What's the actual problem you're trying to solve?

    --
    [ e d @ h a l l e y . c c ]

Re: Decorator(?) classes and inheritance
by duff (Parson) on Jan 30, 2004 at 16:15 UTC
    The actual problem is I have a baseclass A and two children B and C, each overriding a different method in A. How do I have D inherit from B and C and have both overriden methods without having D know about the innards of A, B, and C?

    Um .... you don't. As you've framed it, it seems that D needs to know that B and C have overridden different methods and that it wants both. One way to get D to do what you want is for D to define those methods itself and call the appropriate ancestor:

    # Say B overrode method foo() and C overrode method bar() package D; sub foo { &B::foo } sub bar { &C::bar }
      In most OO languages (from what I have gathered), doing what I want to do should be no problem. I'm running straight into the brick wall of depth-first @ISA searches for methods. I want to change SUPER to be breadth-first. NEXT doesn't do what I want cause it continues the depth-first search, just allowing that the search could backtrack past the re-dispatch. What I really want is use redispatch 'breadth-first'; (as opposed to the default use redispatch 'depth-first';).

      ------
      We are the carpenters and bricklayers of the Information Age.

      Please remember that I'm crufty and crochety. All opinions are purely mine and all code is untested, unless otherwise specified.

        Which other OO languages? The only thing i can think of is the weird C++ thing where the constructors of all the classes you inherited from get called too, but that might actually confuse your problem more than make it simpler. You are looking for your classes to dispatch in a more intelligent way, but I dont think you are giving them enough info to do that with.

        Again, more code (runnable real code not pseudo-code) would really help us in figuring this out.

        -stvn

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://325222]
Approved by Corion
Front-paged by monktim
help
Chatterbox?
and the web crawler heard nothing...

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

    No recent polls found