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

Re: RFC on how I'm doing when it comes to writing objects?

by mbethke (Hermit)
on Feb 04, 2013 at 17:04 UTC ( #1016979=note: print w/ replies, xml ) Need Help??


in reply to RFC on how I'm doing when it comes to writing objects?

Hi LadyAleena,

three things that caught my attention:

  1. Some comments would be nice, otherwise it's hard to deduce what the individual methods are supposed to do without seeing the data and other classes you're using.
  2. Always use 2-argument bless, i.e. bless $ref, $class, otherwise subclassing will not work should you decide to do it later.
  3. The whole blessing ritual doesn't actually gain you anything here as your methods as basically all class methods that you use like glorified procedures. The point of having objects (well, one of them) is to allow for more flexibility with regard to things you have hardcoded right now like the data file name and stuff like %add_lists_data without having to specify them each time. E.g.
    sub new { my ($class, $datafile, $accounts) = @_; return bless { file => $datafile, accounts => $accounts, }, $class; }
    Then you can call methods on that object an reuse the data you passed in on new(), and e.g. cache more data in your instance data as you read them (in case you're likely to need them again later):
    sub acct_data { my ($self, $type) = @_; die "Invalid type, $type does not exist." unless defined $headings +{$type}; return $self->{data}{$type} if defined $self->{data}; return $self->{data}{$type} = { get_hash( file => data_file($self->{file}, "$type.txt"), headings => $headings{$type}, ) } }


Comment on Re: RFC on how I'm doing when it comes to writing objects?
Select or Download Code
Re^2: RFC on how I'm doing when it comes to writing objects?
by Lady_Aleena (Chaplain) on Feb 04, 2013 at 20:40 UTC

    Comments are in. Hope you find them helpful in pointing me in the right direction. I tested the 2 argument bless in the first subroutine. When it was data dumped, I didn't see any change in how the data was stored.

    I went with the first one since it is the most straight forward. It does not have any of my other custom subroutines used in it and stands on its own.

    sub accounts { my ($class) = @_; my @accounts = qw( Lady_Aleena LadyAleena_ABC LadyAleena_CBS LadyAleena_FOX LadyAleena_NBC LadyAleena_SyFy LadyAleena_TNT LadyAleena_USA LadyAleena_TV LadyAleena_eros LadyAleena_home LadyAleena_test ); return bless \@accounts, $class; }

    After adding $class on the last line, the data dumped looked like...

    $VAR1 = bless( [ 'Lady_Aleena', 'LadyAleena_ABC', 'LadyAleena_CBS', 'LadyAleena_FOX', 'LadyAleena_NBC', 'LadyAleena_SyFy', 'LadyAleena_TNT', 'LadyAleena_USA', 'LadyAleena_TV', 'LadyAleena_eros', 'LadyAleena_home', 'LadyAleena_test' ], 'Twitter::Objects' );

    I got the same result without $class on the last line. When I use the subroutine as an object, I would do something like...

    use strict; use warnings; use lib '../files/perl/lib'; use Twitter::Objects; my $accounts = Twitter::Objects->accounts; for my $account (@{$accounts}) { print $account; # and other code I want to run through this loop. }

    I'm not sure what I'm missing.

    Have a cookie and a very nice day!
    Lady Aleena
      Comments are in. Hope you find them helpful in pointing me in the right direction. I tested the 2 argument bless in the first subroutine. When it was data dumped, I didn't see any change in how the data was stored.

      It makes no difference now, nor would it matter with the way you use the returned values. But it will cause you problems later once you start having a proper class hierarchy because it breaks inheritance. Just forget the single-argument blessexists, it's no good.

      I got the same result without $class on the last line. When I use the subroutine as an object, I would do something like...
      my $accounts = Twitter::Objects->accounts; for my $account (@{$accounts}) { ... }

      That's not using it as an object, that's a plain old arrayref that works the same whether or not you bless it.

      As 7stud and others have pointed out, the basic problem is that you haven't organized your code in an OO way so "objectifying" it doesn't work like that. You first have to identify what the "things" are in the system you're building, and what properties they have. "Accounts" would probably be such a thing---a collection of account objects. You'd pass its constructor, "new() usually, a directory that it reads to determine what the accounts are called. If stuff lives in Twitter/$account/... you can generalize that and don't have to hardcode the list. Remember the whole OO dance is supposed to help with code reuse and if the code contains your account list it's not reusable at all. So the instance data would be e.g. a directory and a list of names. Then you could ask the Accounts collection for a list of account names and for an object representing a named account. The Account class wouldn't have to know that its configuration lives in a certain directory hierarchy, it just gets passed a directory by the Accounts collection and reads its files from there. "Lists" would probably be a class of its own that somehow deals with an Accounts object, though I don't understand well enough what a "list" is in this context.

      Playing with the code from perlboot should give you a better understanding of how to change your code organization so it actually benefits from OOP.

        Since I am using these subroutines to return arrays and hashes, they do not benefit from being made into something which looks like an object. I should rename the module to Twitter::Data, take out all of the $class variables and bless uses, and change the calls to it from Twitter::Objects->subName; to Twitter::Data::subName;. The module will still be useful since I will not have to copy and paste the same arrays and hashes in the thirty-odd scripts I have written using the data. Thank you for helping me see where I went wrong in my thinking.

        Have a cookie and a very nice day!
        Lady Aleena

      To expand on what mbethke said:

      Think of each of your accounts as an entity that is self (pun intended) contained - It has information about that account stored in it and has methods that know what to do with that information.

      Each of these entities could be an object, the template for which is defined by the class that you write like so:

      package My::Twitter::Account; use strict ; use warnings ; .... sub new { my $class = shift; my %parameter_hash; my $count = @_; %parameter_hash = @_; croak( "MISSING FILE : \n " . $useage_howto ) unless( $ +parameter_hash{ FILE } ) ; croak( "MISSING HEADINGS: \n " . $useage_howto ) unless( $ +parameter_hash{ HEADINGS } ) ; croak( "MISSING NAME : \n " . $useage_howto ) unless( $ +parameter_hash{ NAME } ) ; $parameter_hash{ DEBUG } = 0 unless( $parameter_hash{ DEBUG } ); my $self = { FILE => $parameter_hash{ FILE } , HERADINGS => $parameter_hash{ HEADINGS } , NAME => $parameter_hash{ NAME } , SOME_USEFUL_INFO_MAYBE => $other_useful_info , DEBUG => $parameter_hash{ DEBUG } , } bless( $self, $class ); dump( $self ) if( $self->{DEBUG} == 1 ); return $self; } ... Other Methods that deal with a SINGLE twitter account here.

      Now that we have something to deal with a single account we can combine these either into a second package or decide to use them in a non-OO way. Lets do the First:

      package My::Twitter::AllAccounts; use strict ; use warnings ; ... blah blah blah sub new { my $class = shift; my %parameter_hash; $parameter_hash{ DEBUG } = 0 unless( $parameter_hash{ DEBUG } ); my @twitter_accounts ; my $self = { TWITTER_ACCOUNTS => \@twitter_accounts , DEBUG => $parameter_hash{ DEBUG } , } bless( $self, $class ); dump( $self ) if( $self->{DEBUG} == 1 ); return $self; } sub add_twitter_account { my $self = shift ; my $name = shift ; my ( $file, $headings ) = _function_that_gets_files_and_headi +ngs(); # This can also be inside the Twitter::Account class. my $twitter_account = new My::Twitter::Account( NAME => $name , FILE => $file , HEADINGS => $headings , ); push( @{ $self->{ TWITTER_ACCOUNTS } }, $twitter_account ) ; return 1; }

      Now we have a way to store a single twitter account and a way to store the collection we can do things like:

      my $twitter_data = new My::Twitter::AllAccounts() ; my @accounts = qw( Lady_Aleena LadyAleena_ABC LadyAleena_CBS LadyAleena_FOX LadyAleena_NBC LadyAleena_SyFy LadyAleena_TNT LadyAleena_USA LadyAleena_TV LadyAleena_eros LadyAleena_home LadyAleena_test ); foreach my $account ( @accounts ) { $twitter_data->add_twitter_account( $account ); }

      Of course the really fun part begins now:

      # Am assuming you move your functions into the correct classes and wri +te some ... But the below is just for illustration. $twitter_data->reload_account_data( $account_name ); $twitter_data->get_sum_of_all_accounts(); $twitter_data->remove_twitter_account( $account ); $twitter_data->temporarily_ignore_accounts( [ account1, account2 ] ); $twitter_data->get_sum_of_all_accounts(); $twitter_data->stop_ignoring();

Log In?
Username:
Password:

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

How do I use this? | Other CB clients
Other Users?
Others chilling in the Monastery: (8)
As of 2014-04-17 07:19 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    April first is:







    Results (440 votes), past polls