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

Object accessors with unknown data

by Amblikai (Scribe)
on Jan 26, 2015 at 10:43 UTC ( [id://1114505]=perlquestion: print w/replies, xml ) Need Help??

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

Hi Monks! I have a quick question with regards to building objects. My search keeps bringing up references to Moose et al, but i don't want to use an external object system. I'm practicing OOP with bog standard perl for my own education!

With that in mind, i'm writing a program where i'm not going to know the data i have when constructing the object. In the past i've always known the name and type of each attribute so i can construct accessors and methods to check that the attribute data is correct etc.

Now i'm stuck though! How do you write accessors for unknown attributes?

Thanks!

Replies are listed 'Best First'.
Re: Object accessors with unknown data
by mbethke (Hermit) on Jan 26, 2015 at 10:56 UTC
    You could do something like this without any eval magic:
    my $o = Anything->new(qw/foo bar baz/); $o->bar; package Anything; use strict; use warnings; sub new { my ($class, @methods) = @_; my $self = bless {}, $class; for my $method (@methods) { no strict 'refs'; *$method = sub { print "this is method $method\n" }; } return $self; }
    But I'd suggest you don't. Classes like this are usually a sign of a problem that's either not analyzed properly or that doesn't benefit from solving it in an OO way. In any case it doesn't exactly make your program easier to understand or debug.

      But I'd suggest you don't. Classes like this are usually a sign of a problem that's either not analyzed properly or that doesn't benefit from solving it in an OO way. In any case it doesn't exactly make your program easier to understand or debug.

      Or hes inventing a class builder, like Mo? Ha

Re: Object accessors with unknown data (hash)
by tye (Sage) on Jan 26, 2015 at 14:25 UTC

    Doesn't sound like you have anything that qualifies as a class. Sounds like you have a hash.

    Accessor methods named after attributes are anti-modular. Using such for ad-hoc attributes provide zero benefit (to go along with the significant drawbacks).

    Now, if you have special behavior that you want to implement, then it can make sense to write a class. But the ad-hoc key/value pairs are not the attributes of the objects. The attributes of the object are whatever pieces of data you need in order to implement the interface. In the simplest case, you build a class with a single attribute, a hash (ref).

    Then the methods you build tend to provide the basic functions that Perl hashes provide:

    my $val = $obj->get($key); my %copy = $obj->get(); my %parts = $obj->get(@keys); $obj->set( $key => $val ); $obj->set( %pairs );

    Those methods can do extra things that just a hash wouldn't do (prevent write access, detect use of non-present keys).

    More likely, you have other behaviors that lead to the need for at least one or two other attributes (which you probably shouldn't build accessors for).

    - tye        

Re: Object accessors with unknown data (eval, code generation)
by Anonymous Monk on Jan 26, 2015 at 10:51 UTC
Re: Object accessors with unknown data
by Anonymous Monk on Jan 26, 2015 at 11:20 UTC

    It's still a bit unclear to me what exactly you want to do... perhaps you could give a short code example of how you expect a class like this to be used. (At the moment I don't see the advantage of a class with dynamically created attributes over a normal hash.)

      So it's potentially not a great candidate for OOP?

      Basically, i'm writing a Spreadsheet to Template program. I've already done it without OOP, but i thought it'd be good to have each line of the spreadsheet as an object.

      In other words, I take a config file with a list of the column headings/titles. The program reads the sheet and finds the title row. Each line of the spreadsheet then becomes a hash where the key is the title, and the value is the spreadsheet value. It's then really easy to build templates from the spreadsheet. I use it a lot.

      I figured i'd have each line of the spreadsheet as an object and then i could have lot's of little methods i could directly call from the template to modify the data. (Split lists etc).

      The problem being i don't know what the title would be so i don't know how i'd access each attribute. The template would know as it would be written by the user in reference to the spreadsheet/config file.

      Bad idea to use OOP?

      Thanks for all your replies

        To me it sounds like a hash would be a perfectly fine representation of a spreadsheet line. If you do it with dynamically created accessors, the program using the spreadsheet-line class would either have to find out which methods are supported, which doesn't offer any advantage over using a plain hash, or it would need to know the names of the columns you'll be using in advance.

        If you want to do it the OO way, you could have a method "fields" or so that returns an array of strings, and one "get_field" that you can pass a field name and it retrieves the value. Unless the class does something smarter on top of that though, that's completely identical to what a plain hash does, just slower and more verbose.

        One of the dangers with dynamic fields is the potential for collision (what if a column is titled "new"?). However, if you wish to try it out anyway, I would use AUTOLOAD. The example in the documentation does exactly what you want (though note the comment that says "this is a terrible way to implement accessors").

        Good Day,
            Dean

        By the way: One advantage that an object might have over a plain hash is that typos in the column names cause errors. However, you can get that effect even with a hash, with the core module Hash::Util:

        $ perl -wMstrict use Hash::Util qw/lock_keys/; my $row = { foo=>3, bar=>7 }; lock_keys %$row; print "$$row{foo}\n"; print "$$row{bsr}\n"; __END__ 3 Attempt to access disallowed key 'bsr' in a restricted hash at - line +5.

        Ok, I see what you're trying to do. Initially I wasn't sure if OO made sense, because part of the reason of having a class is that it presents a well-defined API. If the methods available on an object begin varying, you lose the advantage of a clearly defined API (and with a hash, one can at least use the keys function to see what keys are present). But if the methods are defined by the column names in a spreadsheet, then presumably the user knows what those column names are, so it's not so bad.

        So, you could do it with OO; I the way I would look at it is this: Your object could have a generic get_column method, like ->get_column("column_name"), and a get_columns method that lists the available column names (so far, we've just re-created a hash-like interface). Then, your object could provide dynamically created ->column_name methods as syntactic sugar for ->get_column("column_name"). So far I think this is what you are planning? Problem 1: What if the column name overlaps with an existing method? For example a column named get_column, or isa (inherited from UNIVERSAL). Problem 2: Method calls don't interpolate in strings, while hash lookups do ("$foo->bar" vs. "$foo{bar}").

        So sure, you could use an object, but unless your object would provide more functionality that you haven't mentioned yet, I don't yet see the advantage.

        (P.S. I see mbethke and duelafn posted similar thoughts while I was typing :-) )

Re: Object accessors with unknown data
by sundialsvc4 (Abbot) on Jan 26, 2015 at 15:39 UTC

    I could interpret your post either as meaning “I don’t know what type of data attribute-X might contain,” or, “I don’t know what attributes (and what attribute-names) there might be.”   I’m taking the second interpretation, and therefore concluding (IMHO) that what you are talking about here really doesn’t qualify to be an object attribute (property) at all.

    Instead, I see it simply to be a piece of data that you wish to store-and-retrieve by name, in a store that is associated with a particular object.   You might wish for the object to provide this service in such a way that it is capable of detecting (whatever you consider to be ...) errors, and to throw errors appropriately.   But, basically, these are just named pieces of data, and the object is providing you with the robust, centralized service of storing and retrieving them.

    You could, for instance, define methods such as:


    $obj->set(keyname, value)
    $obj->get(keyname [, default])
    $obj->must_get(keyname)

    It is, of course, possible to use AUTOLOAD tricks to auto-create such accessors on demand, but I suggest that these tricks hide the designer’s intent ... and make it difficult to detect tpyos, which laed to errosr in yuor cdoe taht do not reuslt in the copmile time erorrs taht wolud enbale you to qucikly sopt them.

    If the object needs “to store arbitrary values by arbitrary keys,” perhaps also subjecting those processes to various error-detection steps (such as, discerning whether the keyname is legitimate), then that is a service which the object provides for its clients.   Not a property.   IMHO, true properties are never dynamically created nor dynamically named.   In my experience, that’s a rabbit-hole you don’t want to go down.

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others imbibing at the Monastery: (4)
As of 2024-04-19 02:19 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found