in reply to Often Overlooked OO Programming Guidelines
Some argue that the behavior or subclasses (or subtypes) should not change . . .
I dunno. HTML::Template::Dumper significantly changes the behavior of HTML::Template. If people want to argue that this breaks Liskov, that's fine, but it doesn't stop HTML::Template::Dumper from being useful.
$object->as_string
One important benifit is that YAML/Data::Dumper only show the state of the object's internal data structure, but not lexicals used for storing private data (see below). It might be private, but if it's for debugging or human-readability, it might still be important to print it out. So a mere Dump($obj); is often not enough.
Other observations:
1) One thing I often see is people storing things in the object's structure that should be private, or storing things in lexicals that should be accessible to subclasses. As a general rule, if you want a subclass to get at it, it should be in the blessed reference of the object. Or provide accessors/mutators that are only available to subclasses (not public, unless your design explicitly calls for it). If the data needs to be truely private, put it in lexicals declared at the top of the package.
2) If you have a factory method, do not hardcode the name of the class. Instead, put it in a subroutine, like this:
package My::Foo;
sub BAR_CLASS { 'My::Bar' }
sub bar_factory
{
my $my_class = shift;
my $bar_class = $class->BAR_CLASS;
bless { }, $bar_class;
}
This better supports subclasses, which would otherwise need to override the entire bar_factory method (which could be extremely complex). Now they only need to say sub BAR_CLASS { 'My::Bar::Subclass' } to change exact output of the factory method. (It's the responsibility of the subclass to make sure My::Bar::Subclass implements the same methods as My::Bar).
3) Calling all your own methods should be done in $obj->method( @args ) form. Not method( $obj, @args ) or (worse) method( @args ). Both the bad examples break inheirtance, and the second one isn't a method at all. I could see an argument made for the method( $obj, @args ) form if performance is a concern and you don't care about subclasses or if it's a private method, but I see no excuse for the second form.
4) Document protected methods. Unlike some other languages, Perl doesn't have any very good ways to specify what methods should only be accessible to subclasses (though it has a multitude of hacked solutions). Ultimately, you need to document your protected methods, with warnings of fire coming down from heaven if somebody on the outside uses them. And make sure fire won't come down on ligitimate subclasses.
---- I wanted to explore how Perl's closures can be manipulated, and ended up creating an object system by accident.
-- Schemer
: () { :|:& };:
Note: All code is untested, unless otherwise stated
Re: Re: Often Overlooked OO Programming Guidelines
by Ovid (Cardinal) on Dec 29, 2003 at 23:54 UTC
|
These are some interesting points you made. What stands out to me in your comments is that you've further shown (whether you intended it or not) how Perl's OO, while useful, is a hack and has some serious limitations. I've been working on an piece which shows Perl's limitations and uses Java's OO style as an example (because I know Java better than other OO languages, not because Java is the pinnacle of OO). I think some of your points will fit in quite nicely there.
| [reply] [Watch: Dir/Any] |
|
Hi again Ovid,
I've gone on personal missions to "fix" Perl's hackish
OO. Object::Lexical is one attempt - instance data
is held in lexicals and accessors/mutators are
closures, created with Sub::Lexical.
To quote the POD:
<code>
use Object::Lexical;
use Sub::Lexical;
sub new {
my $counter;
our $this;
my sub inc {
$counter++;
}
my sub dec {
$counter--;
}
my sub inc3x {
$this->inc() for(1..3);
}
instance();
}
This skirts a few issues, which are issues unique to
Perl:
- Different syntax for instance data than other variables
(my, local) makes refactoring code hard - every "$foo" must be changed to "$self->{foo}" manually.
- No help from "use strict" and "use warnings" about
only using a name once. None of the protection of requiring
that a variable be declared first. In short, hash entries
don't get all of the diagnostic helpfullness perl gives
lexicals.
- CPU overhead for hash lookups, memory overhead for
storing hashes.
Each object is given its own stash (namespace) which
inherits from the namespace of the current package.
The stash is populated with closures. Viola!
Thanks to Juerd, by the way, who suggested
creating things and stuffing them into stashes instead
of sticking an AUTOLOAD to proxy to methods stored in
hashes.
Then there is typesafety:
http://search.cpan.org/~swalters/typesafety-0.04/
with its massive userbase of 0 users. This cultural
divide miffs me - no Java user would ever consider
a language that didn't have typesafety, and no Perl
programmer would ever willingly use typesafety.
Actually, I know (or know of atleast) a lot of people that
lost their Perl jobs and had to get Java jobs, and now
would never go back to Perl because of things like the
lack of typesafety and other OO hackishness in Perl,
which just goes to show, people don't know what's
good for them.
-scott
| [reply] [Watch: Dir/Any] |
|
I disagree with the categorization of Perl's OO as a hack. I think it provides the minimal tools as part of the core language, and gives lots of room for addition, restriction, expansion, and factorization. This is evidenced by the wide variety of modules to change/enhance/replace/invert/etc. perl's core object model.
Whether it is good to have such a plethora of ways and means (that you have to go looking for) instead of a Single True Way To Do It, Blessed By The Core, is a separate discussion.
Update: Yes, Perl OO is constrained, and yes, it was grafted onto an existing language; I don't agree that the constraints are a consequence of the grafting. I think they are an intentional design decision (though from recent evidence, Larry wouldn't make the same decisions now).
Update: Seems we just disagree on what hack means. To me it means poor features based on external constraints (whether that be time, backward compatibility, grafting new features onto old code, or whatever).
I think the limitations of Perl OO due to intentional design, not constraint, so I would call it minimalist rather than hacked.
Update: Perl has no useful subroutine prototypes even for non-methods, by the definition of prototypes in other popular languages. The purpose of Perl prototypes (user subs that work like builtins) doesn't apply to object methods.
Update: It's strange to see multiple inheritance cited as a reason to call Perl OO hackish. It's the one feature that isn't what I would call minimalist. It's also hard to see how you could accidentally use it. It's not my fault, Officer, the language made me use multiple inheritance. Click go the closing handcuffs.
| [reply] [Watch: Dir/Any] |
|
| [reply] [Watch: Dir/Any] [d/l] [select] |
|
I disagree with the categorization of Perl's OO as a hack.
I'd classify Perl's OO as a hack: But that doesn't mean it isn't a
useful, flexible, and powerful hack. But it *is* hacked / grafted on
to an existing language, and the imposed constraints shine through the
finished product.
| [reply] [Watch: Dir/Any] |
|
Hi ysth,
"Hackish" is kind of vague, perhaps intentionally.
I don't think anyone is saying perl's OO is bad, just
quick and dirty. The implications of what it is are
more interesting than what it is. Implications:
- Not default. Programs aren't forced to use it. Good,
in my book.
- Not helpful.
- Doesn't encourage good style.
Negitives, point by point:
Not Helpful:: That is, completely unhelpful in diagnostics
and debugging compared with virtually any other OO implementation. Privacy levels, prototypes/typechecking,
and so on exist to help a programmer who can't keep
a large project in his head (or a team of programmers
who can't). If a method doesn't exist I'm trying to
call, I want to know about it at compile time, not
a month down the road when a client clicks on things
in the wrong order and some corner case runs.
Doesn't encourage good style:: Java is adding multiple
inheritance grudgingly, and a lot of people say they
shouldn't. It has willfully not so far. So, Java makes
good style easy and bad style possible. Perl makes
bad style easy and good style possible. Privacy,
finalization, data hiding, strict typechecking
(yes, you heard me) and so on are all possible in Perl,
but they aren't easy. They require additional syntax
and sometimes don't mesh well with other language features.
On the other hand, breaking encapsulation, multiple
inheritance, and other things of questionable style
are all easy in Perl. This doesn't matter if you know
your way around, but a novice programmer assumes that
is easy is what they are supposed to be doing. If the
langauge encourages you to multiple inherit and encourages
you to access data in a class as if it were a hash,
then it must be the best way. People don't read
documentation until and unless all other things fail.
This is a well known principle =)
So, when I say Perl's OO is hackish, what I personally
mean is it is something that kind of happened through
tinkering and building (hacking), not something designed,
specified, tested on focus groups, with second opinions
from industry experts. But that doesn't mean that we
can't continue to hack on it in future versions (eg, 6)
and do RFC processes to form a coherent design,
learn from feedback, visiting Java programmers, and
so on. Hackish isn't fundamentally bad. It fits with
"release early, release often". We just need to hack
on it some more so it is less hackish =)
-scott
| [reply] [Watch: Dir/Any] |
|
|
|