rodee
rodee

Reputation: 3151

Calling methods from a class via another class which uses it

Created a Perl Module:

use ElectricCommander;
use strict;
use warnings;

package EC;

sub new
{
   my $class = shift;
   my $server = shift;
   my $self = {};
   bless( $self, $class );
   $self->{EC} = ElectricCommander->new($server);
   $self->{EC}->login("user", "password");
   return $self;
}
1;

In script, is there a way to call the methods which are in ElectricCommander module? e.g, getJobStatus is present in ElectricCommander, can I do something like below?

use EC;
$obj = EC->new("myserver");
$obj->getJobDetails("1234");

Upvotes: 1

Views: 813

Answers (3)

zdim
zdim

Reputation: 66873

Composition seems suitable here: add methods to EC to dispatch to the other class's methods.

For instance

package EC;

use strict;
use warnings;

use ElectricCommander;

sub new { 
    ...
    $self->{EC} = ElectricCommander->new($server);
    ...
}

sub getJobDetails {
    my ($self, @args) = @_;
    $self->{EC}->getJobDetails(@args);
}

and you can call

use EC;
my $ec_obj = EC->new(...);
$ec_obj->getJobDetails(...);

You can also instantiate ElectricCommander as class data instead of in new so that its object is constructed once, when EC is used. This is a design choice, whether you want EC instances to each have its own ElectricCommander object, constructed as they need it, or whether one object fits all EC objects' needs.

If EC were to truly extend ElectricCommander – to use most of its methods, override some and add more – then you could have it inherit from (subclass) the ElectricCommander.

Most of the time when you run into a need to use a class in another you want composition, sketched above, where a class uses capabilities of another (has-a relationship). Inheritance is suitable when you are building a carefully thought-out hierarchy of classes, where the parent lays down more general or abstract behaviors while its subclasses mostly specialize it (is-a relationship).

Upvotes: 1

ikegami
ikegami

Reputation: 385496

Simply add the following to the EC package:

sub getJobDetails { shift->{EC}->getJobDetails(@_) }

If you have multiple methods of the kind, you can use build the wrappers dynamically.

BEGIN {
   for my $method_name (qw( getJobDetails ... )) {
      my $local_method = sub { shift->{EC}->$method_name(@_) };

      no strict qw( refs );
      *$method_name = $local_method;
   }
}

You could even redirect all calls to methods that don't exist in EC as follows:

use Carp qw( croak );

sub AUTOLOAD {
    my $method_name = our $AUTOLOAD;
    $method_name =~ s/^.*:://s;
    return if $method_name eq 'DESTROY';

    my $remote_method = $_[0]->can($method_name)
       or croak(sprintf("Can't locate object method \"%s\" via package \"%s\"", $method_name, ref($_[0])));

    my $local_method = sub { shift->{EC}->$remote_method(@_) };

    no strict qw( refs );
    *$method_name = $local_method;
    goto &$method_name;
}

Upvotes: 1

melpomene
melpomene

Reputation: 85757

The easiest way to make all methods of ElectricCommander available to instances of EC is to make EC a subclass of ElectricCommander:

package EC;
use strict;
use warnings;

use parent 'ElectricCommander';

sub new
{
   my $class = shift;
   my $self = $class->SUPER::new(@_);
   $self->login("user", "password");
   return $self;
}

1;

Now

use EC;
my $obj = EC->new("myserver");
$obj->getJobDetails("1234");

should Just Work(tm).

Upvotes: 1

Related Questions