It is not reasonable to attribute use of the offending idiom to "mindless
imitation".
I [...] have admitted to repeatedly doing so myself.
[...] you will see others confessing also. tye comes
to mind offhand.
Sorry, wrong.
I've scanned all of the threads you linked to and have to assume that you
have grossly misrepresented what I said in (tye)Re: You Say "Cargo Cult" like it's a BAD thing!:
I've come to hate the term "cargo cult" myself. Many people copied that
constructor code from standard documentation. I thought such copying was
more accurately called "best practices".
You'll note the conspicuous lack of "mindless" or any synonyms thereof.
If I thought the copying in question was "mindless", would I have
described it as "best practices"?
I copied that because I understood it and liked it. There was nothing
mindless about it.
The most direct alternative is to have $obj->new() be
interpretted as "$obj"->new() or something like
"Class::Next=HASH(0xdeadbeef)"->new()
which is just ridiculous! I wonder how many of your classes have this problem.
A more reasonable alternative would be:
sub new {
my $class= shift(@_);
Carp::croak "Usage: ",__PACKAGE__,"->new( ... )"
if ref $class;
# ...
}
but I haven't seen anyone advocating that yet. It is very Perlish to
DWIM in such a case. In the face of the options available to handle this
problem, I support the standard documentation presenting the simple DWIM
work-around that leads to reasonable behavior in more cases. It is a
very simple approach and it is more "robust" rather than "strict". IMO,
it is the best choice (by far) for introductory writings on Perl OO.
In a much more advanced treatment of Perl OO, I'd expect much more
discussion of the problems with that and the reasonable alternatives to
it. I have covered two alternatives above (though I only consider one
to be reasonable). If I didn't want to allow $obj->new()
then I would separate my class methods from my object methods by putting
the class methods in one package and having constructors create objects
blessed into a different package that contains only the object methods.
But that creates quite a few other complications, especially if you want
to "support" willy-nilly inheritance which the beginning Perl OO
documents appear to want you to do.
It is rather funny how much ire is directed at that one line.
It brings out wails about 'cloning' and yet no cloning is done in that
line. You should be complaining about some other line of code that
actually copies data from the old object to the new.
And, since I've read and scanned lots of notes on this topic and don't
recall seeing an example of what to replace that line with, I assume that
you want me to avoid this type of example:
sub Foo::new {
my $class= shift(@_);
return bless [@_], ref($class)||$class;
}
in favor of this:
sub Bar::new {
my $class= shift(@_);
return bless [@_], $class;
}
so that my users can see this:
my $obj= Bar->new(1,2,3);
my $other= $obj->new(4,5,6);
$other->twirl();
Can't locate object method "twirl" via package "Bar=ARRAY(0xbadd0g
+5)"
and have probably no clue what the heck went wrong.
There must be some examples of what alternative I should use somewhere in
one of these long threads. Someone find one so we can see it. It is
certainly rare that such gets shown.
But I choose to use the oft-hated idiom for more than just that reason.
I find that when writing a Perl module for public consumption, there are
usually options that I want to give to potential users of my module.
I want to support my module being used in more than one place. I want
to support each place that uses my module to select the options that make
the most sense in the situation at hand. I expect the people who use my
module to often want to deal with more than one object at once. I think
modules should have unique names and so they tend to have rather long
names. So I like to let the module user just mention the module name
once (in a use statement) and get a customizable factory object put
into a lexical variable with a name that suits their needs.
In fact, I had to go back and make Win32::TieRegistry->new()
even work because I found that people expected that to work despite it never
being mentioned in the documentation for that module.
So instead of having to write, over and over:
my $key= Win32::TieRegistry->new(
"NameOfRegistryKeyToOpen",
{
Delimiter=>"/",
ArrayValues=>1,
# Lots of other options
},
);
I let you just do:
my $key= $Registry->new( "NameOfRegistryKeyToOpen" );
Now, whether that is "clone" or "new" doesn't much matter to me. [
In fact, you can use $reg->open("key") to get the same
thing. I prefer open() but support new() because of the expectations of
Perl programmers. ] But the design of new()/open() that the key
name is required so that it doesn't really make much sense as a "clone"
method since it will be opening a new key. Sure, it copies the options
from the object being used as a factory into the newly created object,
but I would never name such an operation "clone" ("Here is my clone.",
"Um, why doesn't he look just like you?"). You could call it "copy"
but that isn't a great fit either.
I can see where you might not like "new" because you could read it as
"shiny new, having no baggage from anywhere else". Well, I say get over
that interpretation if you are doing Perl OO. You see all of the grief
it is causing you. In my case, use open() instead. But if you think
of "new" as meaning "create an object", then you can use that.
So, you see, in Perl OO, there is no distinction between class methods
and object methods (they are both just subroutines in the same package)
so I like to make my Perl objects have the ability to be both objects and
factories. It is nice to be able to create a not-fully-initialized
object for when it is convenient to set some aspect of the object before
you set others. So it is only natural to be able to use those as factories
as well.
Sure, in some other OO language I would probably have a separate factory
class and would prevent you from creating partially initialized objects
so that I wouldn't have to make each method check "is this one ready yet".
That is often a very good idea. It is like strict.pm, being less
convenient and more strict in hopes of catching mistakes earlier.
Note, there are things about the latest Win32::TieRegistry
that I don't like. For example, exporting the factory object was a bad
idea and I haven't quite worked around that early mistake as much as I'd
like to yet.
But I very much like my ref($class)||$class and I copied
it from the standard documentation after reading about it, understanding
it, and consciously deciding to use it. I understand it even better now
and like it even more.
And it is the best technique for new Perl OO programmers to use in order
to prevent confusion. So I even like it when it is mindlessly copied
(though I'd rather people not program mindlessly, but I don't always get
to control that).
Some other alternatives (which I haven't noticed the haters-of-this-idiom
putting forth) are better for more long-term, big-project, careful, strict
coding projects. Such projects should have careful coding standards and
not be mindlessly copying stuff out of beginner manuals. If this is a
problem that you run into then I suggest you get some coding standards
for your project rather than trying to enforce coding standards on the
rest of the Perl world (since Perl is such a DWIM/convenience language
and Perl OO just doesn't do "strict" very well, IMHO).
- tye
|