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

Re: Re (tilly) 6: To sub or not to sub, that is the question?

by dragonchild (Archbishop)
on Jun 27, 2001 at 00:33 UTC ( [id://91744]=note: print w/replies, xml ) Need Help??


in reply to Re (tilly) 6: To sub or not to sub, that is the question?
in thread To sub or not to sub, that is the question?

Wow. This is turning into an actual conversation. (Something I've found can sometimes not materialize on PM, unfortunately.) Let me respond one at a time.

sub get { my $self = shift; my $class = ref($self) or wantarray ? return () : return undef; # This is just for safety local $_; my @args = $self->coerce_args('ARRAY', @_); my @return = (); if (scalar(@args) < 1) { if (wantarray) { return @return; } else { return 0; } } foreach (@args) { my $key_name = "${class}::$_"; if (exists $self->{$key_name}) { push @return, $self->{$key_name}; } else { push @return, undef; } } if (wantarray) { return @return; } else { if (scalar(@args) == 1) { return $return[0]; } else { return \@return; } } }

Ok. So, 35 lines, not 70. It was 70ish at one point, before I managed to condense a whole bunch. You'll notice the amount of checking I do.

There's a huge amount of stuff I want to add to this, such as switching everything to using a dispatch table, so that people can define their own handlers for a given action, like retrieving or setting an attribute. One of the major criticisms I've received from people looking at this style of abstract base class is that you're stuck using the exact same functionality for every single attribute in a given class. Well, adding dispatch tables would fix that. And, obviously, the idea you mentioned of having an error handler that someone could override needs to be added as well. Combining both things would both expand and contract the actual "getting" function.

There is an exists() function which does your has_attrib() action. I didn't realize we were going to be talking about my abstract base class implementation. (Please look at 88079 for more info.)

I've never used DBI. From my understanding, a SQL statement using the string "NULL" to represent a null string. That would mean that you cannot realistically store the string N-U-L-L in a SQL DB. However, I haven't seen anyone complaining about that, either. (I could very well be wrong, as I don't work with SQL on a day-to-day basis.)

Irregardless, I still maintain that every API forces you to accept certain restrictions on your allowable data-set that aren't in the base language. In using that API, we accept those restrictions (which should be very clearly stated in the accompanying documentation.)

Replies are listed 'Best First'.
Re (tilly) 8: To sub or not to sub, that is the question?
by tilly (Archbishop) on Jun 27, 2001 at 04:40 UTC
    Yeah, every so often I get into a conversation around here. Always seems to surprise people.

    Anyways as I say here I don't personally tend to use get/set methods much for personal reasons. After reading the article that I reference, you might find yourself reconsidering assumptions on OO design as well. But that is neither here nor there.

    First some detailed comments. On clear programmer mistakes (calling a method as a function) you are not raising an error. That is an error and should be caught ASAP. Secondly I don't like the overloading of the return value. If I am returning to a scalar the result of getting a list of values, that will work fine until that array only has one element, then my code breaks. DWIM is something I am cautious of. If getting single values works, then single values will come up in scalar context. Thirdly attributes do not play well with inheritance in your model unless everyone is everywhere using your get/set routines. To me that is too restrictive.

    About DBI, your understanding is only somewhat correct. If you wish to build a string SQL statement you would use NULL for null and "" for an empty string. However when querying from the database you get NULL as undef. Conversely if you use placeholders as DBI recommends, then you slam an undef value into the execute and you will populate the database with NULL values. In short, undef is Perl's version of NULL and both the native input and output respect that.

    Finally I tend to consider APIs that needlessly restrict your data representations as somewhat to very broken. They are, at the least, an area of fragility that can lead to broken code. Certainly I work to avoid that, and I think my code is better for that decision.

    Now here is (untested) a fragment of code that corresponds roughly to your get, implemented closer to how I would do it:

    use Carp; sub get { my $self = shift; my @ret = map $self->_get($_), $self->coerce_args(@_); wantarray ? @ret : $ret[0]; } sub _get { my $self = shift; my $field = shift; exists $self->{"attrib_$field"} ? $self->{"attrib_$field"} : $self->raise("Object '$self' does not have attribute $field"); } sub raise { shift; goto &confess; }
    Since this is untested, there may be obvious typos. But this fixes all of the issues I identified and does some other things. First of all since raise is a method call, it is overridable. Secondly _get is a method call that can be overridden. Classes that want to have attributes accepted that don't fit with this _get can just write their own method, that then defaults to SUPER->_get. That way they can add things like computed fields.

    Basically the watchword here is that if you want to get configurability, insert a method call. Place them roughly where you think people would want "hooks" into what you are doing, and you will get the configurability without having to code anything. An alternate way of thinking about it is that a method call already is a lookup into a dispatch table. So rather than code your own dispatch table that people need to interact with by your rules, just make a method call...

      *is very thoughtful*

      I read the JavaWorld article you referenced. It made me realize something - All the OOP I've ever done has really been procedural programming using inheritance and some, very basic encapsulation. I've been thinking about OOD as a way of collecting data in a stronger struct, one that has functions associated with it naturally.

      But, after seeing the concept "An object is defined by its capabilities" ... I need to revamp my thinking a whole lot.

      While I appreciate your ideas on the get() function, and would implement many of them if I actually implemented this, I want to explore further what exactly is involved in "true OOD", if such a thing exists.

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others pondering the Monastery: (5)
As of 2024-03-28 23:16 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found