Beefy Boxes and Bandwidth Generously Provided by pair Networks Bob
Clear questions and runnable code
get the best and fastest answer
 
PerlMonks  

Re: Creating Common Constructor

by DeadPoet (Scribe)
on Jul 10, 2003 at 16:08 UTC ( #273030=note: print w/ replies, xml ) Need Help??


in reply to Creating Common Constructor

After looking into broquaint idea of using a Class Factory, which I really like, I have transformed the code into the "readmore" listing below. However, I am still in search of any suggestions on how to improve.

Your thoughts, comments, and suggestions are more than welcomed

DeadPoet

#---------------------------------------------------------- # Filename Zoo.pm #---------------------------------------------------------- # ---------- # | Zoo | # ---------- # | # | HASA Animal # v # ---------- # | Animal | # ---------- # ^^ # / \ # ISA Animal/ \ISA Animal # / \ # / \ # ---------- ---------- # | Camel | | Lama | # ---------- ---------- #---------------------------------------------------------- package Zoo::Zoo; use UUID; use base qw( Zoo::Animal::Animal Zoo::Animal::Camel Zoo::Animal::Lama +); use strict; #---------------------------------------------------------- # Create the Class defaults. #---------------------------------------------------------- { my $_class_defaults = { _oid => '???', _type => 'zoo', _Camel_count => 0, _Lama_count => 0, _test1 => '????' }; sub _class_defaults { $_class_defaults } sub _class_default_keys { map { s/^_//; $_ } keys %$_class_defa +ults } } sub new { my ( $caller, %arg ) = @_; my $class = ref($caller); my $defaults = $class ? $caller : $caller->_class_defaults(); $class ||= $caller; my $self = bless {}, $class; # Generate an Object ID my $sref_oid = Zoo::Zoo->_gen_oid(); # Populate the new object with either passed parameters # or the defaults. foreach my $attrname ( $class->_class_default_keys ){ if ( exists $arg{ $attrname } ){ $self->{"_$attrname"} = $arg{$attrname}; } else { $self->{"_$attrname"} = $defaults->{"_$attrname"}; } } $self->{ _oid } = $$sref_oid; return $self; } sub factory { my($self, $class, $args) = @_; my $obj = "Zoo::Animal::$class"->new( $args ); $self->{"_${class}_count"}++; return $obj; } sub _gen_oid { my ( $o_uuid, $o_id ); UUID::generate($o_uuid); UUID::unparse( $o_uuid, $o_id ); return undef if ( $o_id eq '' ); # catch if the unparse failed. return \$o_id; } sub _get_camel_count { my ( $self ) = @_; $self->{ _Camel_count }; } sub _get_lama_count { my ( $self ) = @_; $self->{ _Lama_count }; } sub print_obj { my ( $self ) = @_; foreach ( keys %{ $self } ) { print STDOUT "$_ -----> $self->{ $_ }\n"; } print STDOUT "\n\n"; } sub DESTROY { my ( $self ) = @_; printf ( "\n%s : $self cleaning up.\n", scalar ( localtime ) ); } 1; __END__ #---------------------------------------------------------- # Filename Animal.pm #---------------------------------------------------------- package Zoo::Animal::Animal; use strict; sub print_obj { my ( $self ) = @_; foreach ( keys %{ $self } ) { print STDOUT "$_ -----> $self->{ $_ }\n"; } print STDOUT "\n\n"; } 1; __END__ #---------------------------------------------------------- # Filename Camel.pm #---------------------------------------------------------- package Zoo::Animal::Camel; @Zoo::Animal::Camel::ISA = qw( Zoo::Animal::Animal ); use strict; #---------------------------------------------------------- # Create the Class defaults. #---------------------------------------------------------- { my $_class_defaults = { _oid => '???', _type => 'camel', _color => 'grey', _legs => 4 }; sub _class_defaults { $_class_defaults } sub _class_default_keys { map { s/^_//; $_ } keys %$_class_defa +ults } } sub new { my ( $caller, %arg ) = @_; my $class = ref($caller); my $defaults = $class ? $caller : $caller->_class_defaults(); $class ||= $caller; my $self = bless {}, $class; my $sref_oid = Zoo::Zoo->_gen_oid(); foreach my $attrname ( $class->_class_default_keys ){ if ( exists $arg{ $attrname } ){ $self->{"_$attrname"} = $arg{$attrname}; } else { $self->{"_$attrname"} = $defaults->{"_$attrname"}; } } $self->{ _oid } = $$sref_oid; return $self; } sub DESTROY { print "Destroying the camel Object\n"; } 1; __END__ #---------------------------------------------------------- # Filename Lama.pm #---------------------------------------------------------- package Zoo::Animal::Lama; @Zoo::Animal::Lama::ISA = qw( Zoo::Animal::Animal ); use strict; { my $_class_defaults = { _oid => '???', _type => 'lama', _color => 'white', _legs => 4 }; sub _class_defaults { $_class_defaults } sub _class_default_keys { map { s/^_//; $_ } keys %$_class_defa +ults } } sub new { my ( $caller, %arg ) = @_; my $class = ref($caller); my $defaults = $class ? $caller : $caller->_class_defaults(); $class ||= $caller; my $self = bless {}, $class; my $sref_oid = Zoo::Zoo->_gen_oid(); foreach my $attrname ( $class->_class_default_keys ){ if ( exists $arg{ $attrname } ){ $self->{"_$attrname"} = $arg{$attrname}; } else { $self->{"_$attrname"} = $defaults->{"_$attrname"}; } } $self->{ _oid } = $$sref_oid; return $self; } sub DESTROY { print "Destroying the lama Object\n"; } 1; __END__ #---------------------------------------------------------- # Filename Zoo.pl # Just a simple test script. #---------------------------------------------------------- use Zoo::Zoo; use strict; my $o = Zoo::Zoo->new(); for ( my $i = 1; $i<= 5; $i++ ) { my $o_camel = $o->factory( 'Camel' ); $o_camel->print_obj(); my $o_lama = $o->factory( 'Lama' ); $o_lama->print_obj(); } print STDOUT $o->{ _type } . " has " . $o->{ _Camel_count } . " camels +\n"; print STDOUT $o->{ _type } . " has " . $o->{ _Lama_count } . " lamas\n +";


Comment on Re: Creating Common Constructor
Download Code
Re: Re: Creating Common Constructor
by jmanning2k (Pilgrim) on Jul 10, 2003 at 17:16 UTC
    Still some things here that don't make sense from a OO perspective.

    Like you said previously, a Zoo has Animals, an Animal is not a Zoo. Therefore, Animals shouldn't really be a subclass of Zoo.

    Your implementation still hard codes the animal types into the Zoo class. If you want to add a new type of animal, there are a lot of changes to make.
    Instead, the count methods should be members of the Animal subclasses. Zoo should have an array of objects of type Animal, and be able to get the count for each one.

    Here's my ideal design:

      jmanning2k, I am interested in your design thoughts, as well may be others, please post additional information. One other aspect that is not illustrated here but will be in my final post is the implementation of persistence by way of Storable.


      Additional Information:

      ** The Zoo object will act as a mapper object to all other created object and will be persistent (Storable) thus maintaining its state (knowledge) of all Animals.

      ** The Animal objects will be persistent thus maintaining their state.

      ** The Zoo object when loaded must be able to re-animate (load) all previously known Animals.

      Questions:

      ** How does this additional information change or affect your design?

      DeadPoet

        Doesn't change much.

        Your Zoo package can have a reanimate method that loads some data from a file or DB. I'll use a flat text file as an example:
        Stored Zoo data: Llama - Feet:4 - Color:Tan Camel - Feet:4 - Humps:2
        The reanimate method will go through this data and do:
        #[pseudocode - will not compile] foreach $line (<FILE>) { # Get $name and hash data from file # $name, $animalstats (hashref) my $newanimal = new Animal($name, $animalstats); push (@allanimals, $newanimal); }
        This repopulates your zoo with the stored information.

        For further enhancement, I'll elaborate on my Animal::Collection idea now.
        Your zoo should really contain several collections of animals. This way all animals of a similar type (or habitat) can be grouped together. This makes it easier to count all the animals in a particular type. I would do this with some sort of hashtable containing arrays of animals. (or animal collection objects).

        To do this, change the @allanimals array to a hashtable. Key it on the animal type (or habitat, etc).
        __DATA__ Llamas: Llama1 - data data data Llama2 - data data data Camels: Camel1 - data data data Camel2 - data data data __CODE__ # [pseudocode] foreach (my $groupname = from_datafile()) { ## Llamas, Camels, the collections. unless (exists $allanimals{$groupname}) ## Create a new collection if you don't already have one for that + animal type. my $newcollection = new Animal::Collection($groupname); $allanimals{$groupname} = $newcollection; } my $collection = $allanimals{$groupname}; # Now work with that collection. Add all your animals in that group. foreach $animal (animals_from_datafile()) { ## llama1, llama2, etc. $collection->add_animal(new Animal($animal, $animalstats); } }
        Aside: I'm assuming Animal->new is a factory method.
        package Animal; sub new ( my $name = shift; my $data = shift; if($name == 'llama') { return new Animal::Llama($data); } elseif ($name == 'camel') { return new Animal::Camel($data); } else { warn "Unknown animal type\n"; return new Animal::Generic($name, $data); } }
        So, you now have an $allanimals hash that holds all your animals. Each Animal type has it's own subtype, and therefore it's own stats, as per your original requirements.
        To work with the animals, you iterate through the collections and animals - use something like:
        foreach $type (keys $allanimals) { my $num_of_animals = $allanimals{$type}->count_collection(); # A met +hod in Animal::Colelction foreach $animal ($allanimals{$type}->get_animals()) { ## Using methods defined by the Animal interface ## But implemented in the Animal::Llama, Animal::Camel, etc subcl +asses. my $name = $animal->get_name(); $animal->feed(); } }
        The important thing to remember here: A zoo can hold any type of animal. You should never mention a specific animal type in your Zoo object. This makes it very difficult to add new animal types later. There are only 3 places where a specific animal is mentioned: in your datafile, in the subclass, and in the factory method.

        ~Jon

Log In?
Username:
Password:

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

How do I use this? | Other CB clients
Other Users?
Others drinking their drinks and smoking their pipes about the Monastery: (7)
As of 2014-04-16 04:32 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    April first is:







    Results (413 votes), past polls