当前位置: 动力学知识库 > 问答 > 编程问答 >

perl - Delegation over Inherited object

问题描述:

Here is our current implementation:

We have a base class - Coew Device. There are many classes which are inherited from this Core Device class - having specific feature implemented. These derived classes are futuer the base classes for other type of devices. Simply, it's like the following structre:

Core Device --> LevelA Devices == > LevelB Devices .... ==> Level Z Devices

To work on these leaf level devices, end application creates an object of this device and start calling the APIs which are available from Device Level Z till root.

e.g.

my $iPhone = new DeviceLevelZ (NAME => 'iPhone');

Currently for handling a sub which is not implemented in any of the tree path, AUTOLOAD is used at Core Device base class. So, AUTOLOAD at base class - Core Device - perform the necessary action based on the sub called from end application.

Now, we are planning to go away from AUTOLOAD and would like to adopt the Delegation using Conway's Class::Delegation module.

To implement this, I updated the Core Device base class by defining the delegation like this

use Class::Delegation

send => 'getFoo',

to => sub { print "Hello Worlld!! I am foo\n" },

;

But when I call the sub getFoo from device object, it's not delegating to this delagtor.

Here is a small program mentioning the same behavior:

my $device = new DeviceLevelZ();

$device->get();

$device->getDelegation();

package ResourceX;

use Class::Delegation

send => 'getDelegation',

to => sub { print "Hello Worlld!! I am foo\n" },

;

sub new

{

my ($class, %args) = @_;

my $self = {};

bless $self, $class;

return $self;

}

sub get

{

print "I am a resource\n";

}

package DeviceLevelZ;

use base qw(ResourceX);

sub new

{

my ($class, %args) = @_;

my $self = $class->SUPER::new(%args);

bless $self, $class;

return $self;

}

1;

网友答案:

It's attempting to delegate, but you're sending back the result of print. As 1 is not blessed, so you can't call a method on it, and the delegation fails at that point.

I think you're missing the point of specifying a sub for a delegation point. It's supposed to return the delegate. Damian sez:

A subroutine may also return a reference to an object, in which case the subroutine is delegated to that object (rather than to an attribute of the current object). This can be useful when the actual delegation target is more complex than just a direct attribute. [emphasis mine]

So if you add say a package like this to your code:

package DoesDelegation;
our $Delegate = bless {}, __PACKAGE__;

sub getDelegation {
    say 'You just called me!';
}

And you modify the delegation sub like so:

    ...
    to => sub { 
    print "Hello Worlld!! I am foo\n";
    $DoesDelegation::Delegate;
    },

You won't see a failed delegation--because now you've passed back something that can handle the message 'getDelegation'

Also, to give you an understanding of what is happening in the background: If you modify your code like this:

use Class::Delegation
    send => 'getDelegation',
    to => sub { 
    use Data::Dumper;
    warn "In delegate \@_ :\n", Dumper( \@_ ), "\n";
    warn "\$1='$1'\n\$2='$2'\n";
    print "Hello Worlld!! I am foo\n";
    $DoesDelegation::Delegate;
    },

You'll see this:

In delegate @_ :
$VAR1 = [
          bless( {}, 'DeviceLevelZ' ),
          'getDelegation'
        ];

$1='DeviceLevelZ::'
$2='getDelegation'

Thus there are two sources of data. In @_ you get 1) the public object and 2) the name of the method. Whereas, you get other data in $1 and $2 which is the full name of the method split up by package (stash name?) and name.

A Suggestion for Class::Delegator

If you have a delegation that is that complex, then you could use a sub. But I don't see that in your description. If you don't have something that complex, then I recommend David Wheeler's stripped-down version Class::Delegator, where I have found this to be true, when crediting Conway's module:

Damian Conway's brilliant module does ten times what this one does--and does it ten times slower.

I also recommend that you use the declarative structure of the use statement to dispatch your delegations, instead of trying to delegate to a sub that that routes it to the proper delegate, as you appear to do here.

If you have a delegate in mind to handle getFoo, then instead, do this:

...
send => 'getFoo', to => '{foo_getter}'

Moose

A third and final way to do it, is Moose Delegation:

package Website;
use Moose;

has 'uri' => (
    is      => 'ro',
    isa     => 'URI',
    handles => [qw( host path )],
);

So in specifying the delegates to the aggregate object, you specify the delegation in the handles parameter.

分享给朋友:
您可能感兴趣的文章:
随机阅读: