Hamish Whittal <[EMAIL PROTECTED]> writes: > Robin, > > Thanks so much for the effort you put into this reply....I just have a > few more questions...see below.
<snip> > > What it does do is show how I solve the problem of the parent's > > classes 'new' not knowing about data members needed for the children. > OK. This is the problem I was battling to understand. See, a cs2600 has > a whole host attributes (characteristics) that I need to set. Since a > device is-a (or may-be-a - to be more correct) cs2600, I additionally > need to set attributes that pertain to a specific device. > Example: > A Cisco2600 has the following attributes (i.e. when I query it, I can > obtain the following information from it): > - nvRam > - swVersion > - hwVersion > - systemObjectID > - sysUpTime > > But, I cannot just send the queries into the ether(net). I need to > direct the query to a device (of type Cisco 2600). The specific device, > should have all the properties (attributes) of a Cisco 2600 (above), but > additionally have an IP address and a Community string. > So, a device (if it's a Cisco 2600) should inherit the characteristics > of cs2600, but be able to initialise itself with an IPAddress and > Community string additionally. Clear? Ok. Some properties will be specific to a Cisco 2600, others will be generic for everything of the 'Device' type. There will be yet other properties specific to other kinds of devices. Just put the cs2600-specific properties in cs2600.pm, the generic properties in NetDevice.pm, and other type-specific properties wherever they go. Then, using the example code below, you can: my $cisco = new NetDevice::Cisco::cs2600(ipaddress => "200.12.1.50", object_id => 57); cs2600.pm does not refer to an 'ipaddress' field anywhere, yet this constructor works, and I can then call: $cisco->ipaddress; # returns "200.12.1.50" This is inheritance at work - my $cisco object has all the properties and methods of a NetDevice object. You might also have a set of properties that are common for all 'Cisco' type devices. This might indicate that you need a 'Cisco' object with the properties common to cs2600, cs5200, cs1100, etc. <snip> > > foreach (keys %{$fields}) { > > if (exists $attr{$_}) { > > $self->$_($attr{$_}); > > } > > } > OK. I get this bit. Basically, call the sub's to initialise the IP > address and the Community. V. clever. The important bit here is that keys %{$fields} not only has the fields from %valid_fields in the NetDevice object, but *also* includes any fields from child objects returned by their 'valid_fields' method. > > > > $self->_init; # class-specific init code > > But, why do you init after you have set the attributes? I don't have much use for it here, but maybe some subclass of NetDevice needs to make sure objects have *either* a ipaddress *or* a systemid. Or maybe a subclass of NetDevice initializes an SNMP connection to the device in question so it can be polled for more data. <snip> > > NetDevice/Cisco/cs2600.pm: > > > > package NetDevice::Cisco::cs2600; > > > > use strict; > > use warnings; > > > > use NetDevice; > > > > our @ISA = qw/NetDevice/; > > > > my %valid_fields = (object_id => undef, > > ); > Create the attributes for the Cisco2600....check. > > > > sub valid_fields { return (shift->SUPER::valid_fields(), %valid_fields) } > Now, I am not sure what is happening here. You seem to be running the > subroutine valid_fields from the SUPERCLASS, and returning the hash > returned by that, as well as a valid_fields hash created above with > object_id being undefined. This therefore returns a double hash - one > from the superclass and one from this class. I'll expand the above subroutine from a one liner to make things more clear. Here's what's happening: sub valid_fields { # this overrides the valid_fields sub found in NetDevice.pm my $class = shift; return ($class->SUPER::valid_fields(), # SUPER calls NetDevice, # and returns it's %valid_fields %valid_fields) # valid_fields from this package } The part I think you're missing is that hashes in list context return a list of the form (key1, value1, key2, value2) - so in our example the above is expanded to something like this: (ipaddress => undef, community => undef, object_id => undef) The order of the pairs can vary, but a key is always followed by it's value. So when NetDevice::new() calls 'valid_fields', it gets the above list, and assigns it to a hash - which gives you all the fields appropriate to your NetDevice::Cisco::cs2600. Magic. > > sub object_id { > > my $self = shift; > > my $id = shift; > > > > if (defined $id) { > > $self->{object_id} = $id; > > } > > > > return $self->{object_id}; > > } > I know you are doing the same thing here as in NetDevice above, but I > never see this sub called. How does it populate the object_id then? object_id is called from NetDevice::new - the keys of the %fields hash at that point in the execution are (ipaddress, community, object_id). Since the 'object_id' in the below call to new is passed into the %attr hash, new calls $self->object_id, finds it in NetDevice::Cisco::cs2600, and runs it. > > my $cisco = new NetDevice::Cisco::cs2600(ipaddress => "200.12.1.50", object_id => > > 57); > > Here you call the new from NetDevice, as there is not new in cs2600.pm. > Would it be possible to explain this a little more? If it helps, this line could also be written: my $cisco = NetDevice::Cisco::cs2600->new(ipaddress => "200.12.1.50", object_id => 57); Since the NetDevice::Cisco::cs2600 package ISA NetDevice, here's what happens when you call new NetDevice::Cisco::cs2600 in the code above- perl looks for new() in cs2600.pm, and doesn't find it. Next, it looks in NetDevice.pm, finds new(), and calls it with the following parameters: ('NetDevice::Cisco::cs2600', 'ipaddress', "200.12.1.50", 'object_id', 57); Now to go through 'new' again: sub new { # from NetDevice.pm my $class = shift; # $class = 'NetDevice::Cisco::cs2600' my %attr = @_; # becomes the hash described above my $fields = {$class->valid_fields}; # becomes the hash(ref) # described above my $self = bless { %{$fields} }, $class; # $self is now a 'NetDevice::Cisco::cs2600' object, which is a # hashref-type object, with the keys described above, and 'undef' # for all the values. foreach (keys %{$fields}) { # qw/ipaddress community object_id/ if (exists $attr{$_}) { # only ipaddress and object_id exist in # %attr this time we called new() $self->$_($attr{$_}); # calls ipaddress() from this file # (NetDevice.pm), and find object_id # in cs2600.pm } } # One of the important bits in the above is that while ipaddress() is # found in NetDevice.pm, in this case it is a method of the # NetDevice::Cisco::cs2600 *object* ($self) we just created. $self->_init; # _init() from cs2600 if one exists, or _init() # from NetDevice.pm return $self; # a NetDevice::Cisco::cs2600 object. } One last comment - while I've emphasized that we are creating a NetDevice::Cisco::cs2600 object, it is *also* a NetDevice - you can check that with 'isa()', which is available to all objects: print "cisco ISA NetDevice::Cisco::cs2600\n" if $cisco->isa("NetDevice::Cisco::cs2600"); print "cisco also ISA NetDevice\n" if $cisco->isa("NetDevice"); print "cisco also ISA Monkey\n" if $cisco->isa("Monkey"); > Thankyou again so much for taking time out to answer this long-winded > question. No problem. OOP is hard. -RN -- Robin Norwood Red Hat, Inc. "The Sage does nothing, yet nothing remains undone." -Lao Tzu, Te Tao Ching -- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]