Beefy Boxes and Bandwidth Generously Provided by pair Networks
The stupid question is the question not asked
 
PerlMonks  

Re: Help creating a function

by ELISHEVA (Prior)
on Feb 16, 2011 at 11:35 UTC ( [id://888497]=note: print w/replies, xml ) Need Help??


in reply to Help creating a function

I want a general solution as it should work for any packages.

For that to work, you would have to deal with the problem on both ends: not only would you need an object loader, but also you would need to ensure that all of your objects had the same construction semantics as your loader.

In Perl some objects are backed by hashes and some are not. There are objects that are backed by arrays, scalar references, code references, io handles, and much more. You can't even assume that the objects store their own data. For a while something called inside-out objects were fashionable - instead of storing their data they used their reference id or some other id to look up their field values in arrays and hashes stored at the class level.

Even if the data is stored in a per-object hash, you wouldn't necessarily want to set the fields directly. The constructor might be doing some error checking or generating derived data, or other setup that will be missed if you bypass the constructor method and assign values to hash keys directly.

Existing partial solutions

There are some partial solutions already done for you, but they depend on your object's storage format. For example, if you dump class data in a YAML file and then reload it, the reloaded data will automatically be blessed into classes. In this case bypassing the constructor isn't so much of an issue because the dump process took a snapshot of data that was already set up by the constructor. You can also use Data::Dumper, Data::Storable and Data::Serializable (with YAML/Storable/Dumper serialization) this way.

If you are loading data from a database and you know there is a one-to-one relationship between objects and rows in the view you are retrieving, you could use selectrow_hashref or selectall_arrayref($sql, { Slice => {} }) to load in a hash and then bless it to whatever class you wish, e.g.

my $obj = bless($dbh->selectrow_hashref("SELECT ..."), 'Employee');

Since we are loading from a database we can assume (?) that the database's stored procedures and table/field constraints have already ensured that the data is validated. If that is something that interests you, then I recommend that you open your database handle with RaiseError => 1 so that exceptions are thrown if the class fails to load. If you are willing to have all your objects use Class::DBI you can even get automatic conversion of queries to objects. Inheriting from Class::DBI would also let you do additional derivations and validations after the automated population of data from the database.

Rolling your own

If you wish to roll your own solution, it will pay to think long and hard about the scope of your object loader tool. Where is your data coming from? What storage mechanisms do you want to use? Is this going to be used only by classes you write? Or will other people be using this object loading tool for their own classes? Are you going to load only objects written for the tool's conventions, or do you need to load any arbitrary object ever written for Perl?

If you are only loading your own objects and you are the only person ever using this, I would recommend just deciding on a constructor convention and sticking with it. One word of warning though: experiment.

Despite its common use, there are a lot of drawbacks to the constructor model in your example (creating an empty object and setting properties). One big issue is that validations that rely on the values of two or more fields can be tricky to get right. Instead of one constructor that looks at both values at the same time, you have to have write a validation/derived value calculation routine and then have both property setter methods call that validation anytime the property is set. Sounds easy enough, but it isn't.

It is far too easy to forget to call the validation in one of the setter methods. You may never even notice the bug until you have corrupted data. It can also lead to performance issues: if two values contribute to a derived value and they are set separately, then the derivation is recalculated each time a property is set. Even if you get both properties at once and could in theory set them together, you can't because your interface only lets you set one property at a time.

If this loader has to be used by others, it is important that whatever constructor semantics you choose, you will need to document them like crazy and also be prepared to spend time teaching and explaining them to others (or to the tech writer if you are working together with one).

If this loader has to work with any old object, then strict rules about how to create objects won't work for you. You'll have to come up with a mechanism to (a) define the constructor semantics for a class (b) store that definition in your object loader (c) retrieve the definition when an object of a particular class is requested (d) apply that definition to that particular hash and class.

Even if you are the only one to use this, it would be a good idea to document it carefully. You'll find (speaking from experience here) that there are quite a number of different construction strategies you'll have to consider and hence quite a number of different ways to define your rules. You will in effect be writing a domain specific langauge.

I don't mean to scare you off, but I just want you to understand that what you are asking is not just "a function". Especially if you go for the most general solution, you run the risk of spending more time writing and debugging the code than you would hand crafting constructor calls for your project.

Also unless you know the whole area of object construction in depth, you are likely to end up with a tool that gets productivity gains in one area only to take them away in others. Object have lots of ways of being built because they support a very wide variety of data and processing needs. Any object loader tool that doesn't respect that will end up tying programmers into knots forcing them to program to the tool rather than to the real performance and data flow constraints of their problem domain

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others making s'mores by the fire in the courtyard of the Monastery: (2)
As of 2024-04-26 00:25 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found