[Omaha.pm] Class::Date - change once set

Jay Hannah jay at jays.net
Wed Oct 26 19:13:39 PDT 2005


On Oct 26, 2005, at 9:32 AM, Kenneth Thompson wrote:
> Actually, I'm not so sure this is as bad a thing at it seems on the
> surface. The perldoc states that it
> exposes the internal pieces via an array of parts/formats:
>
> -------------
> INTERNALS
> This module uses operator overloading very heavily. I've found it quite
> stable, but I am afraid of it a bit.
> A Class::Date object is an array reference.
> -------------
>
> Given that the array has been exposed and documented on purpose, I
> wouldn't think that this type of manipulation would be bad. Even if 
> they
> change the array, it's setting one object's internals equal to the 
> other
> objects internals. Is that the same as accessing the internals for
> manipulation?

In my opinion, breaking encapsulation is always a bad idea and should 
be avoided at all costs.

What if...
- The class changes to a hash implementation a year from now?
- The package is (or someday starts to) keep counts inside its scope or 
UNIVERSAL so copying that value breaks something?
- One of the elements of the array ref is supposed to be unique? Memory 
pointer? Database ID? Any persistence ID?
- the package overrides the assignment operator, causing weird things 
to occur?

Even if the module author tempts you, do not partake of the forbidden 
fruit.

Breaking encapsulation just begs your code to break some day, a year 
from now, when you've forgotten all about this class and have much 
better things to be working on than some mystery thing that "used to 
work". Unnecessary risk with no upside. Fragile code that will shatter 
time and time again.

> Of course the hard count reference (0..9) should be a
> little more dynamic in case the array size is increased for some 
> reason.

Yes, I agree -- If you're going to jump out of an airplane w/o a 
parachute, then landing on your feet is probably a good idea. I predict 
the fall will still kill you, though. -grin-

Better yet, take a parachute with you! (Leave encapsulation intact!)

> However- what about a whole different approach? Maybe a referenced
> workaround?
>
> use Class::Date qw( date );
> #Clone a date object into and existing date object
>
> my $d1 = Class::Date->new("1971-01-01");
> my $d1R = \$d1;
> my $d2 = Class::Date->new("2000-01-01");
>
> print "[".$d1."][$d2]\n";
> stuff($d1, $d2);$d1 = $$d1R;
> print "[$d1][$d2]\n";
>
>
> sub stuff {
>     my ($d1, $d2) = @_;
>     if ($d2 > $d1) {
>       $d1R = \$d2;   # <---- I want to overwrite the existing $d1 here
>     }
> }
>
> And for discussion- why
>
>     my ($d1, $d2) = @_;
>
> and not
>
>     ($d1, $d2) = @_;
>
> ? (by the way... The second way works the way you wanted it to)

In both of those examples you're reusing a variable that's been scoped 
to be global throughout your program. As we all know, globals are 
another evil root of maintainability doom.

Pretend our little demo is part of a larger (real world) program by 
expanding the demo to 2 files (a .pl and a .pm) and "use strict;" and 
you'll see that strict won't let you play those particular reindeer 
games:

----
$ cat j2.pl
use strict;
use Class::Date;
use Printer;

my $d1 = Class::Date->new("1971-01-01");
my $d2 = Class::Date->new("2000-01-01");

Printer::print($d1, $d2);

$ cat Printer.pm
package Printer;
use strict;

sub print {
    print "[$d1][$d2]\n";
}

1;
$ perl j2.pl
Global symbol "$d1" requires explicit package name at Printer.pm line 5.
Global symbol "$d2" requires explicit package name at Printer.pm line 5.
Compilation failed in require at j2.pl line 3.
BEGIN failed--compilation aborted at j2.pl line 3.
----

The

    my ($x, $y, $z) = @_;

idiom is how you know for sure that your sub/method has its own scoped 
copies of or references to the arguments that were just handed it.

Two things I learned setting up this demo:

(1) "use strict" doesn't detect scoping problems if you define multiple 
packages in the same file. Doh! I don't do this in real life anyway, 
but it surprised me.
(2) You have to "use strict" in both files to get it to complain. I 
thought once you "use strict"'d anywhere in your program that it would 
protect you across all your packages. Not true! Note to self: DO 
explicitly type "use strict;" into EVERY Perl file to make sure you 
don't burn yourself!

Encapsulation good. Globals bad.

I'll solve my original problem by storing the Class::Date objects in a 
location that both my main and my package can access. (Attributes of a 
commonly accessible class...)

I hope that helps,

j
long winded soap box dude

P.S.  Damian Conway's OO Perl book delivers the encapsulation lecture 
far better than I can.




More information about the Omaha-pm mailing list