Beefy Boxes and Bandwidth Generously Provided by pair Networks
XP is just a number

Advice on OO Program structure & organization

by yoda54 (Monk)
on Dec 11, 2004 at 08:52 UTC ( #414082=perlquestion: print w/replies, xml ) Need Help??

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

Dear Monks,

I'm an OO newbie needing some advice. I need a program that will read from txt files for use in an objects data structure. My confusion is where in the OO design shall I load data files? Do I create functions in the same class? I want to do something like this:


Data files: ------------------------------- pinto.template: horse_power = 50 price = 10 corvette.template: horse_power = 200 price = 11000 ------------------------------- package Car; use strict; sub new { my $type = shift; $self->{misc} = undef; $self->{blah} = undef; bless $self, $type; } sub model { my $self = shift; if (@_) { $self->{model} = shift } } sub search { open files parse for data & return... init a HASHDB ? } ------main----- use Car; my $my_car = Car->new; $my_car->model("pinto"); $my_car->search("horse_power"); and later.... $my_car->model("corvette"); $my_car->search("horse_power");

Replies are listed 'Best First'.
Re: Advice on OO Program structure & organization
by BrowserUk (Patriarch) on Dec 11, 2004 at 10:16 UTC

    Have a Class method (not a instance method) called (say) loadFrom(), that takes a filename, and returns an array of instances.

    Have another Class method (say) findMatching(), that takes a reference to that array and a partially completed instance of that class, and returns one or more matching instances from the array.

    use Car; my @cars = Car->loadFrom( 'cars.dat' ); my $partial = Car->new( horsepower => 200 ); my @matches = Car->findMatching( \@cars, $partial ); print $_->details for @matches;

    If you move your data from a flatfile to a DB, you pass a DB handle (or credentials) to loadFrom() and everything else still works.

    As you add attributes to your class, the search code continues to know how to compare your instances, and your instantiator knows how to construct (partial) instances. The internals can change as needed, with the external inteface remaining the same, except where you need deal with new attributes.

    Examine what is said, not who speaks.        The end of an era!
    "But you should never overestimate the ingenuity of the sceptics to come up with a counter-argument." -Myles Allen
    "Think for yourself!" - Abigail        "Time is a poor substitute for thought"--theorbtwo         "Efficiency is intelligent laziness." -David Dunham
    "Memory, processor, disk in that order on the hardware side. Algorithm, algorithm, algorithm on the code side." - tachyon
      I'd do one better than that. Pass it a file handle (glob) instead of the file name. That way, you can have it read from aynthing that's filehandle driven, like a tcp/ip socket. If it's done right, that the class only reads the minimum amount of data FOR a car, you can put multiple cars in one file and initialize multipl objects like that. Or if it makes you happy, mix-and match data in your data stream so that you can read a car, then a person, then a drink, then a cow.. you get my drift.

      Give me strength for today.. I will not talk it away..
      Just for a moment.. It will burn through the clouds.. and shine down on me.

Re: Advice on OO Program structure & organization
by ikegami (Patriarch) on Dec 11, 2004 at 09:05 UTC
    I'd leave the file stuff outside of the class. How the data is organized in a file has nothing to do with a Car. Create a seperate function in your program (maybe in the CarTemplate package) which gets the values, and creates a Car from them.

      I too prefer this approach. Most of my object constructors take a list of pairs of attributes, check them for sanity, and set useful defaults if needed. They don't load or save or anything like that.

      Instead, I use an external class that knows how to load, save, and instantiate many types of objects. In pattern language, this is a Factory.

      It's especially nice because it aggregates the loading and saving into one place, conceptually unrelated to the objects themselves, which otherwise would have non-behavioral code mixed in with normal methods. It also allows the flexibility to change the serialization format almost trivially.

        A minor squibble. Not a criticism at all, as there are multiple right ways..

        Sometimes it depends on your user interfaces to your object. Perl's oop, on a basic level, has no security behind it, so any method you use for changing an attribute or state of an object for your factory, is available to the developer using your object. When you maintain state internally to your object, that may be a bad thing. i.e. if you have a linklist like object, and you create a method called set_size which just sets an internal variable for get_size to report back a number, some chucklehead, wrongly, and idiotically, if not documented, may try and use this method. Now your get_size will be off. But as they say, if you have no buisness being there, don't be surprised if I have a shotgun. :)

        I only raise the point, as some other OOP implementations, java, ruby, python, c++, have different levels of encapsulation ability and security. In perl, it may be a silly thing, but in others, it may matter more.


        Give me strength for today.. I will not talk it away..
        Just for a moment.. It will burn through the clouds.. and shine down on me.

        Classes that serialize themselves (toXml, toYaml, whichever) typically have the saving/loading embedded in that class. Keeping code in one place prevents the need to dual-maintain as many files. However, yes I agree that if multiple types of serialization are needed this could clutter up the class somewhat. The answer is: there is no good answer, pick what you like.

        I find a Car::fromXml($foo) method to be pretty elegant. A factory is definitely useful when we need to churn out things that conform to a sequence or that way conflict with one another, such as if we need to roll cars off the line with increasing VIN numbers :)

Re: Advice on OO Program structure & organization
by csuhockey3 (Curate) on Dec 11, 2004 at 13:00 UTC
    Don't believe it gets any better than that, BrowserUk ++

    If you do not already own one, pick up Object Oriented Perl (foreword by the great merlyn). It's a fantastic OO reference.
Re: Advice on OO Program structure & organization
by Thilosophy (Curate) on Dec 12, 2004 at 03:28 UTC

    That is against the OO-approach. Your car object is supposed to represent a real-life object (a car). You cannot change the model of a car. You can only get a new instance (a different car) for the new model.

    How about this: A class CarPark which you can initialize from your file and query for Car objects.

    my $park = new CarPark( filename => 'blah'); # this will read the file, and (unless the file # is too big or can change in between) save it into # an internal structure suitable for the lookups # you are going to do (probably hash keyed on car model) # so that you have to read it only once. my $car = $park->get("pinto"); print $car->horse_power; $car = $park->get("corvette"); # a new instance print $car->horse_power
      What about turning a Porsche into a RUF? :)
Re: Advice on OO Program structure & organization
by redhotpenguin (Deacon) on Dec 12, 2004 at 19:13 UTC
    I had to do something similar to this but my object data was in XML files. In that case I used XML:Simple but for this application I would use Config::Tiny as follows.

    car.txt contents: [pinto] horse_power = 50 price = 10 [corvette] horse_power = 200 price = 11000 #!/usr/bin/env/perl use strict; use warnings; use Car; my $car = Car->new('corvette'); print "My ", $car->model, " has ", $car->horse_power, " horsepower!"; 1; package Car; use strict; use warnings; use Config::Tiny; my $CONFIG = './car.txt'; sub new { my ($class, $model) = @_; my $cfg = Config::Tiny->new(); $cfg = Config::Tiny->read($CONFIG); die warn "No such model $model!\n" unless exists $cfg->{$model}; return bless { model => $model, %{$cfg->{$model}} } , __PACKAGE__ +; } sub model {die warn "Can't change model!" if @_ > 1; $_[0]->{model}; } sub horse_power {$_[0]->{horse_power} = $_[1] if @_ > 1; $_[0]->{horse +_power}; } sub price {$_[0]->{price} = $_[1] if @_ > 1; $_[0]->{price}; } 1;
Re: Advice on OO Program structure & organization
by gmpassos (Priest) on Dec 12, 2004 at 19:35 UTC
    OO is not just design, OO is for reusability. So, don't write any read and write methods, just use another Class/Module that already exists, in this case IO::All, and call it:
    use Class::HPLOO ; class Foo { use IO::All ; ## Object initializer: sub Foo ($file) { $this->{file_data} < io($file) ; } }
    Class::HPLOO is just a module that enables this kind of OO syntax for Perl. But I recommend to learn the pure Perl style, since with it you are free to do anything. But this style above save me a lot of time. ;-P

    Graciliano M. P.
    "Creativity is the expression of liberty".

      This is bad for many reasons, namely you have no way of instantiating a Foo without using a file, in this case. There is nothing wrong with adding functions for read/write, as this is common in serialization idioms (they'll even call other methods of child objects, etc).

      As the more versed in softare design will know, using an existing module has nothing to do with OO, nor does OO have anything to do with reusability. It is commonly parroted that non-OO code is not reusable, and it may better be said that non-namespaced code is not reusable. It has nothing to do with object-orientation. Structure-oriented code can easily handle reuse, OO when yielded properly has greater advantages of course -- such as polymorphism.

        The code above is just an example! duh! About OO and reusability. Well, OO doesn't guarantee reusability, but I dare you to make good reusability without OO.

        Graciliano M. P.
        "Creativity is the expression of liberty".

Re: Advice on OO Program structure & organization
by Rudif (Hermit) on Dec 12, 2004 at 22:02 UTC

    It looks like you are creating a database of info about cars - of car models? or of car instances?
    Either way, I think that you need at least two classes - a CarDatabase and Car.
    Info on individual cars should be stored in instances of class Car.
    Class CarDatabase should have a collection (array or hash) of Car instances, and methods for loading info from datafiles and storing it back to datafiles, a.k.a. serialization, and methods for searching, filtering, listing, e.t.c.


Log In?

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://414082]
Approved by zejames
Front-paged by exussum0
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others taking refuge in the Monastery: (5)
As of 2023-12-11 23:00 GMT
Find Nodes?
    Voting Booth?
    What's your preferred 'use VERSION' for new CPAN modules in 2023?

    Results (41 votes). Check out past polls.