Beefy Boxes and Bandwidth Generously Provided by pair Networks
more useful options
 
PerlMonks  

Design Question - I've gone object mad!

by Cagao (Monk)
on Apr 05, 2011 at 21:54 UTC ( [id://897614]=perlquestion: print w/replies, xml ) Need Help??

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

All my code these days is object oriented, maybe to the extreme I sometimes think!

In a recent project I'm creating objects to model pretty much everything, be it a customer, a company, a training course, even a basic list of industries ( just id => name ).

Starting with a basic " id => name " scenario, I have a My::Industry class with a get method...

my @industries = My::Industry->get();

Which will return a list of industry objects. All very good so far.

I then introduce filtering such as...

my ($industry) = My::Industry->get( id => 7 );

Note the fact that the interface remains the same, always returning a list, even if only 1, which I kinda like.

A common get() method to save having a get_all(), get_by_id(), get_by_name() arrangment.

Now extending this to the My::Customer class, works exactly the same.

I can even ask for SQL-style modifiers...

my @jobs = My::Customer->get( sex => 'M', employed => 1, sort => [ +'id', 'desc' ] );

You can see I've extended the interface to allow different sort directions, which is simple enough, and makes logical sense, to me anyway.

However, I now want to select objects that are OLDER than a specified DateTime - simply passing in a DateTime would look like I was looking for perfect matches...

my @jobs = My::Customer->get( signed_up => DateTime->now, employed +=> 1 );

So I'd need something to indicate the direction I'm after, perhaps something like...

my @jobs = My::Customer->get( signed_up => [ DateTime->now, '>' ], +employed => 1 );

Maybe I have just answered my own question here, but after some constructive ideas, or what other people have done in similar scenarios.

This is all in Moose btw, incase there's some helpers I'm not yet aware of.

Thanks for reading, and any advice you care to share.

Replies are listed 'Best First'.
Re: Design Question - I've gone object mad!
by ikegami (Patriarch) on Apr 05, 2011 at 22:04 UTC

    You are building an ORM. That's no small task! Perhaps you should use an existing one (e.g. DBIx::Class). That even lets a database handle the guts of the methods for you! If you don't want to run a database server, you can use SQLite database.

      I thought this all felt a bit like "surely someone's done this before!" :)

      Tho I feel any use of DBIx::Class would exist even under this level of interface - I nearly want html/TT coders to be able to call my methods, but certainly don't want them this close to the inner-workings of my models.

      We're already heavily using MySQL, all old legacy code that's 10 years old, with no objects in sight! This is all new code I'm writing to refactor the old scripts.

        Not only has it been done before, it has been done much better than you will do it. If you were experimenting, I’d say, “Hells yeah, go for it and have fun!” But if this is really for work, “You are making a huge mistake and you are punishing the future developers who will hate you for it if they have any sense at all.”

        DBIx::Class (just for one, and there are other excellent ORMs in Perl) works out of the gate with all the common DBDs, it has oodles of support tools for auto-generation of code, introspection, inflation, versioning, helpers, constrainers, dumping, deployment, etc, and it comes with a community, a stable history, and a test suite that concludes as of this moment with–

        Files=251, Tests=5141, 152 wallclock secs ( 2.20 usr 1.28 sys + 125.6 +3 cusr 13.35 csys = 142.46 CPU) Result: PASS

        If you can conjure 5,000 passing tests for your new code, I withdraw my objection.

Re: Design Question - I've gone object mad!
by sundialsvc4 (Abbot) on Apr 05, 2011 at 22:12 UTC

    A few, maybe ...

    Remember that you’re out to design a computer program, not to design a world (nor even a simulacrum thereof).   It is easy to “design objects for the sake of their being objects,” but without a clear design-purpose for the “object metaphor” in your actual software.   If you cannot clearly articulate why a particular object, property, or method belongs in your design, nor why it makes the design “–er,” take it out immediately.

    (IMHO...) The most important thing to “encapsulate” is logic, not data.   There is usually no practical advantage, for example, in exposing “SQL tables, rows and columns” as objects (i.e. an “ORM”), and in a great many instances it results in code that is (in my experience) much less maintainable.   Logic that ought to have been encapsulated (such as, any and all dependencies on “exactly how facts are being represented, stored and changed in a database”) winds up being exposed, and scattered hither-and-yon.   “The mere fact that you can handle hot things using soft, cuddly object-looking mittens, does not mean, de facto, that it is wisdom to fling hot objects all over the place.   Perhaps you’ve merely packed your spaghetti in lots of little boxes.”

    Take “objects” (and every other metaphor known) just as far as they can usefully take your design, and not one whit farther.   If the metaphor makes your code cleaner, easier to understand, less “coupled,” more “compartmentalized” and so on and on and on ... proceed.   But when they come to an end ... stop.

    Your instincts tell you that you have lost your way.   Undoubtedly, your instincts are correct, and it is wise that you stopped and timely asked for guidance.   “Trust your feelings, Luke.”

      Fully aware of these, not gone quite 'that' far, everything has a purpose and is modelled appropriately.

        And, good Monk, despite my long-winded reply, I didn’t mean to suggest otherwise.   :-O !!

Re: Design Question - I've gone object mad!
by Voronich (Hermit) on Apr 06, 2011 at 00:14 UTC
    All my code these days is object oriented, maybe to the extreme I sometimes think!

    That alone is worth an upvote.

    Personally I've found that modelling data objects (or real life objects) literally is less useful than I'd been taught as an OO whelp growing up.

    Far more useful has been to model processes or semantics. i.e. what do you really DO with "Industry" rows? If you start with the literal model then you have to redesign the semantic layer on top of that anyway and you end up with nasty object interrelation soup that abstracts a simple sql statement.

    Do you really have such breadth of real world operations on this data that you need universal 1:1 access to the database objects modeled as objects? (You may I suppose.)

    That said, you may be committed to this course of action for one reason or another, which would make this "observational commentary" rather than a suggestion ;)

    $0.01

    Me

      I have come across this thought from my fellow monks in the past, yet I still can't seem to see how, possibly not seeing the woods through the trees.

      In my example, a My::Customer may work in a My::Industry. They'll need to select a My::Industry on a form.

      This all leads me to needing to get a list of possible industries, and tying the two together through their IDs.

      Somewhere else I may need to get all My::Customer objects that registered last month, last year, 2 weeks ago, etc, hence the need for some sort of...

      My::Customer->get( registered => [ DateTime->new( year => 2011, month +=> 4, day => 1 ), '>' ] );

      ...approach.

      I'd love to sit down and talk this idea of modelling processes more than the objects themselves with a fellow monk, but unfortunately I currently work pretty much alone, while training up 2 others.

      I presume my original approach to be "ok", I think I've asked a similar question before and it usually goes on this same tangent of the modelling rather than the interface.

        Somehow, I do not like the '<' string. Cannot you supply a sub ref that will return the values you need, i.e. similarly to sort:
        My::Customer->get( registered => [ DateTime->new( year => 2011, month +=> 4, day => 1 ), \&greater ] );
        You can than define between etc.
        Indeed. I'll chime in with the reassurance that there's little "wrong" with the modelling of 'physical' objects.

        It tends to be a bit heavy weighted later on. But refactor it a bit at a time. For instance, everything you describe sounds ok. But I don't have to work with it, so it's tough to figure out where the sticking points are. All I can really advise is to pay very close attention to "I wish I could just...." thoughts. Optimistically write it top-down as far as you can, then the design difficulties will emerge in a pretty clear way.

        Either way, sounds like your thinking on the right lines.

        Me

      Just a quick addition, the My::Customer object will translate columns, join multiple tables together, etc, to return a fully fledged My::Customer object.

      I'm not just blindly modelling a database table.

      Like I say, this modelling is, in my eyes, a layer above the database model (if i were to introduce DBIx::Class, for example).

        From this particular description it sounds like you misunderstand what DBIx::Class does out of the box and what it can do with plugins/customization.

        Have you tried it? Do you know it does all those joins and such, in deep, flexible, beautiful ways with chained resultsets, and can do things like manage transactions with nothing but variable scope? Twice in the last few years I’ve been stuck with legacy code written under the auspices of NIH syndrome and I really hold a grudge against the developers who made those choices and wasted weeks and weeks of my time that could have been productive, just chasing bugs and dealing with edge cases. For the future “me”s out there, I’m begging, investigate your options. There are many.

Re: Design Question - I've gone object mad!
by trwww (Priest) on Apr 07, 2011 at 04:10 UTC

    You're reinventing DBIx::Class in general, and as far as your specific question, you are reinventing SQL::Abstract, the tool that DBIx::Class uses to generate SQL.

      I'm hearing this a lot, but I'm not aware of DBIx::Class being able to send queries to Solr, for example.

      There are a lot of alternative storage/index/search engines around and we don't want to be tied to using a database.

      As I've said, the model I'm working on is above any db layer.

        I'm not aware of DBIx::Class being able to send queries to Solr, for example

        It looks like it does to me:

        DBIx::Class::Indexer::WebService::Solr

        Even if it dosen't do exactly what you want, personally I'd rather take that and modify it to get it to do what I want instead of start from scratch.

        As I've said, the model I'm working on is above any db layer.

        Its your project so you know and I don't, but since you're here asking for advice I'll give it: DBIx::Class works above the db layer. From here it looks like you are reinventing wheels instead of figuring out how to add in hooks to existing frameworks.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://897614]
Approved by ikegami
Front-paged by luis.roca
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others browsing the Monastery: (7)
As of 2024-04-18 07:27 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found