Beefy Boxes and Bandwidth Generously Provided by pair Networks
good chemistry is complicated,
and a little bit messy -LW
 
PerlMonks  

Inherited Object Constructor Question

by PyrexKidd (Monk)
on Sep 11, 2011 at 20:26 UTC ( [id://925374]=perlquestion: print w/replies, xml ) Need Help??

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

Hello Monks,
I am attempting to code a plug-in driven application. (The initial outline is here but is largely irrelevant to the issue at hand.)
Since the construction of Objects is generic and almost identical, it makes the most sense to use a base class then inherit the constructor methods into the plugin(subclass); I would like to make crating plugins as easy as possible by removing all redundant code.

I have this working mostly, the problem is, I am required to have the 'set_id' method in every subclass I create.
Here is what I mean:

File: test.pl

#!/usr/bin/perl use common::sense; use lib './'; package main; my $monitor_plugins = SNMPMonitor->new(); my @plugins = $monitor_plugins->plugins(); print "Plugin: $_ \n" foreach @plugins; my $c = SNMPMonitor::Plugin::Atest->new('1.2.3'); use Data::Dumper; print Dumper $c; print "\n"; print "Name: " . $c->name . "\n";; print "Full Name: " . $c->full_name . "\n"; print "OID: " . $c->plugin_oid . "\n"; #$c->set_oid(); # print "\n"; # print "Name: " . $c->name . "\n";; # print "Full Name: " . $c->full_name . "\n"; # print "OID: " . $c->r_oid . "\n"; print Dumper $monitor_plugins; package SNMPMonitor; use Module::Pluggable require => 1; #instantiate => 'new'; sub new { my $obj_class = shift; my $root_oid = shift; my $class = ref $obj_class || $obj_class; my ($name) = $class =~ /::Plugin::(.*)/; my $self = { name => $name, full_name => $class, root_oid => $root_oid, plugin_oid => '', }; bless $self, $class; for my $datum (keys %{$self}) { no strict "refs"; *$datum = sub { shift; # XXX: ignore calling class/object $self->{$datum} = shift if @_; return $self->{$datum}; }; } eval {$self->set_oid()} if $self->can('set_oid'); return $self; } #sub set_oid { # my $self = shift; # no strict "vars"; # $self->plugin_oid($plugin_oid); #}

File: ./SNMPMonitor/Plugin/Atest.pm

package SNMPMonitor::Plugin::Atest; use common::sense; our @ISA = qw(SNMPMonitor); my $plugin_oid = '1.2.5'; sub set_oid { my $self = shift; $self->plugin_oid($plugin_oid); } 1; __END__

If I comment out the set_oid method in the subclass SNMPMonitor::Plugin::Atest and uncomment it in the base class SNMPMonitor I get an error that $plugin_oid doesn't exist in SNMPMonitor. (hence the no strict "vars"). Authors note: This warning is indicative that I haven't done something right, I'm just not sure what.
It is very important to set the OID in the plugin subclass and not the SNMPMonitor super class because each monitor will be responsible for their own OID space; not to mention I don't want to have to maintain an additional database of OID -> plugin mappings, nor do I believe that it is necessary.

Thanks in advance,
Any help is greatly appreciated.

(Note: In the final version every thing in package SNMPMonitor will be abstracted to SNMPMonitor.pm, it is here to ease development so I'm not in and out of the two files all the time.)

Replies are listed 'Best First'.
Re: Inherited Object Constructor Question
by Arunbear (Prior) on Sep 11, 2011 at 23:07 UTC
    In SNMPMonitor you can define set_oid as
    sub set_oid { my $self = shift; $self->plugin_oid($self->plugin_oid_val); }
    and in SNMPMonitor::Plugin::Atest define
    sub plugin_oid_val { '1.2.5' }
    Then you should get the desired behaviour.

    BTW why not use Moose for this? For the purpose of reuse, it seems cleaner to provide set_oid via a role, since a Plugin doesn't seem like a convincing subtype of a SNMPMonitor (e.g. Liskov substitution principle)

      This solution is so simple, I had to laugh out loud. My co-worker thought I was nuts until I showed him your reply. It wasn't immediately obvious, but having seen the solution it is a definite /facepalm moment.

      To answer your question, I'm not using Moose because I hadn't considered it, this is primarily due to my lack of understanding of Moose. Looking at the module for Moose::Manual::Roles I can see what you are saying, but I'm not if it becomes more clear. As a sysadmin the majority of my programming is functional, I'm relatively new to OO Perl and OO Design.
      I guess my biggest issue is I am not sure how to load my "plugins" with moose.

      As far as naming goes, how is SNMPMonitor::Plugin::MySQL_Monitor not a subclass of SNMPMonitor? (I grant you ATest is not really a subclass of anything, but I don't use Foo::Bar for testing by default). That is, each Plugin will have its own methods that are independent of each other, but they will eventually a return a value to the engine (test.pl, due to be renamed).
      That being said, perhaps it would be more clear if all of the constructors and similar methods were in SNMPMonitor/Plugin.pm? since SNMPMonitor represents the entire SNMPMoitor framework?

        I guess my biggest issue is I am not sure how to load my "plugins" with moose.

        I use Module::Pluggable::Object:

        package My::Project::HasPlugins; use MooseX::Role::Parameterized; parameter 'namespaces', isa => 'ArrayRef', required => 1; role { my $p = shift; my $namespaces = $p->namespaces; has 'plugins', is => 'ro', isa => 'ArrayRef', lazy_build => 1; method _build_plugins => sub { return [ Module::Pluggable::Object->new( instantiate => 'new', search_path => $namespaces, )->plugins ]; }; } 1;

        ... and in a class which needs to use plugins:

        package My::Project::SomeClass; use Moose; with 'My::Project::HasPlugins' { namespaces => [ 'Some::Plugin::Namespace' ] }; ... 1;

        You can do this without the parametric role, but it worked really well for my project.


        Improve your skills with Modern Perl: the free book.

        If you replaced an instance of SNMPMonitor with an instance of SNMPMonitor::Plugin::MySQL_Monitor, would your program still run correctly? If not, then the latter should not be a subclass of the former (which is what the Liskov Substitution Principle says).

        Yes it would be clearer to have methods common to all plugins in a base plugin (or role).

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others goofing around in the Monastery: (9)
As of 2024-04-18 13:25 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found