Beefy Boxes and Bandwidth Generously Provided by pair Networks
XP is just a number
 
PerlMonks  

Re: Modification of @ISA at run time

by nothingmuch (Priest)
on Sep 01, 2005 at 07:04 UTC ( [id://488313]=note: print w/replies, xml ) Need Help??


in reply to Modification of @ISA at run time

My solution is always to deglobalize.

If $sun_rises_in_west is a global condition, encapsulate it.

Sometimes you want to calculate things for the parallel universe you don't live in. What is your user going to do then?

To emulate global behavior, just make your default instantiation take into account the fact that the default $sun_rises_in_west_flag is x and not y, and save it as instance data.

To dispatch based on instance data, the most useful technique is delegation - encapsulate your instance data in an object, and have your object as that object to polymorphically get_sunrise_time based on the kind of object it is.

Playing with multiple inheritence implies that your object hierarchy is not only a definition, it also encapsulates state.

While this feels very cool and meta-meta-meta-fun, it's usually not very good for:

  • testing
  • stability
  • readability
  • maintainability

Update: blazar requrested that I added an example, based on TheDamian's reply..

## this starts like damian's example: # Rational helper class... package Astro::Data::Scientific; use base Solar::Data; use base Lunar::Data; # <define methods here> # "Differently rational" helper class... package Astro::Data::Mythological; use base Astrological::Data; # Pre-empt normal methods use base Astro::Data::Scientific; # <overload any methods here that the # heavens dictate should behave differently> # Delegating class package Astro::Data; sub new { my $class = shift; my $sun_rises_in_west = @_ ? shift : $default_sun_rises_in_west; my $delegate = ("Astro::Data::" . ($sun_rises_in_west ? "Mythologi +cal" : "Scientific"))->new; bless { delegate => $delegate, }, $class; } # define like this or use Class::Accessor sub sunrise_delegate { $_[0]{delegate}; } # define like this or use Class::Delegation sub get_sunrise_time { my $self = shift; $self->sunrise_delegate->get_sunrise_time(@_); }
The difference between this and the factory approach is not very evident at this scale. This has some disadvantages, namely in being a harder to set up, and less efficient, but it scales better when you have several variadic sets of methods... For example, if $sun_rises_in_west and $number_of_moons > 1, and so on, it might be easier to make a delegate for each, instead of combining each possibility into it's own class.

Update 2: I should mention what I really like about delegates - they allow you to keep things better separated.

Instead of your entire set of capabilities (methods) being mushed into one object, you get a higher degree of separation. Whether this boundry is later blurred for convenience as far as code using the delegating object is concerned is irrelevant - the implementation of the delegating object stays cleaner.

This is useful when you have conceptual objects that perform many roles. For example, I have a system where data can be used as resources in a resource allocator.

Instead of making the data, which already has the mess involved with persistence and what not added in, inherit yet another class (the one for a resource), i simply make a new resource class, that is a thin wrapper around this object. This object then becomes a delegate of the resource class.

This has a conceptual simiarity to the MVC model. The model part shows you what the is structured like. The control decides what to do with the data, andn the view, through the controller, displays the model. The delegating object is a bit like a controller, and the delegate is a bit like a model in this sense.

-nuffin
zz zZ Z Z #!perl

Replies are listed 'Best First'.
Re^2: Modification of @ISA at run time
by xdg (Monsignor) on Sep 01, 2005 at 14:02 UTC

    Apropos of the recent discussion "How to differentiate hash reference and object reference?", how do you recommend handling isa when using delegation like this? Do you also define an isa that passes the isa call through to the delegate?

    sub isa { my ($self, $class) = @_; return $self->{delegate}->isa($class); }

    If so, it's another reason why people should avoid this:

    if ( UNIVERSAL::isa( $obj, 'Astro::Data::Mythological' ) ) { # whatever }

    -xdg

    Code written by xdg and posted on PerlMonks is public domain. It is provided as is with no warranties, express or implied, of any kind. Posted code may not have been tested. Use of posted code is at your own risk.

      Well, the reimplementation of isa is probably the way to go, but normally I've found that the greater the all-knowingness of an object, the less I need to introspect it.

      In a sense the behavior is transparent, instead of using a delegate there could have been an if/else on the member data in the single classes get_sunrise_time

      My point is that when designing in this direction, usually you want to pretend that the object is the same, and the delegate is just an implementation detail. In situations when the role is truely different you don't want to instantiate from the same class anyway, you have have two top level classes, one for west-rising suns, and the other for east rising ones.

      In fact, that is exactly what the delegation model is - it's two independant classes, completely unbound to each other, but interchangable, and an object which can reuse them to give you an alternative with a single point of entry.

      Good examples of this kind of reuse can be dug up by querying Any... These are modules which usually wrap around several modules which do the same role, but have different features. The wrapper modules hide the details of the difference from the user, letting the user give the generic module more kinds of input, without caring which bunch of code really does the job at the end.

      To wit, after saying

      my $a = Archive::Any->new($archive_file);
      I don't care if what I got was reblessed into a different class, or wheter it has a delegate, or whether it uses MMD inside. In fact, it probably isn't Archive::Tar at all, in any sense, since it doesn't provide it's full interface.

      When ->isa checks are used with respect to the delegate for the prupose of type checking (what kind of archive format do you encapsulate, mr Archive::Any instance?), then it's perhaps a design problem, because not caring is the reason we wrapped it in the first place.

      -nuffin
      zz zZ Z Z #!perl
        When ->isa checks are used with respect to the delegate for the prupose of type checking (what kind of archive format do you encapsulate, mr Archive::Any instance?), then it's perhaps a design problem, because not caring is the reason we wrapped it in the first place.
        But one may find desirable to have some common functionality plus extra type-specific features, e.g.
        if ($this->isa 'cool_archive') { print $this->name, " is a cool archive.\n", <<EOT; You also have the following cool features: ... (more cool features) EOT # interactively ask user to pick one... }
        Or is such a design to be considered inherently risky?

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others surveying the Monastery: (6)
As of 2024-03-28 10:27 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found