in reply to Re3: Constructor/Factory Orthodoxy
in thread Constructor/Factory Orthodoxy

1. my $class = ref $class || $class; This, to me, says that this method can be used as either an instantiation method or a cloning method. (In other words, can be called either as Foo::new or $foo->new.)

Although I suppose you could call Foo::new with a separate class argument, I think this form of class naming is for Foo->new (and $foo->new, as you say). But your complaints hinge on the $foo->new() form:

They will both return you a new object, yes. But, the two methods are not the same internally. One creates a new object from values you give it (plus possibly some defaults) and the other creates an object from the object that clone() was called upon.

This doesn't mean you can't have new() do both things. But, I've never seen a new() function that had the offending line and be a cloning method. If it doesn't do both things, you have a NO-OP that is potentially confusing to your maintainer.

You comments regarding cloning make good sense. However, I've always considered cloning (perhaps arbitrarily) to be a separate balliwick from constructors and factory methods. I've never considered $foo->new() to indicate cloning, but rather a better looking way to say (ref $foo)->new(). Where do you think these two potential expectations of new() functionality arise (constructor/factory vs cloning)?

The various comments about Base classes knowing nothing about their children are very convincing, however.

Your comments regarding maintenance are poignant (I've been saddled with maintenance from time to time).

Thanks for the elaboration,
Matt

Replies are listed 'Best First'.
$class = ref $class || $class
by rir (Vicar) on Feb 26, 2003 at 21:58 UTC
    I've never considered $foo->new() to indicate cloning, but rather a better looking way to say (ref $foo)->new().

    I accept the $class = ref $class || $class as a Perl-specific idiom and so don't find it confusing. It is a bad idiom though. In itself it reads as nonsense: class is the ref of class or class. Worse is the habit that you have acquired: to think that $foo->new; is shorthand for (ref $foo)->new;. This is bad because they don't mean the same thing unless you know the internals of new. Also if you don't know the type of $foo you should explicate this fact with ref $foo not leave the issue in doubt.

    As this use is idiomatic Perl it is not too confusing once you realize a codebase uses the idiom. However it is sloppy, it spreads in a codebase and is some work to undo. I write something inside my new (for no reason) that allows you to imply different meanings (for no reason) through the different usages that I allowed (for no reason). Sloppy.

    It buys nothing. It will give pause to the better OO programmers who are new to Perl.

      Howdy!

      I usually take 'ref $class || $class' to allow me to use an extant object to call the constructor instead of uaing a class name.

      Of course, we always check the documentation before assuming we know how the module works, right? And we module authors make clear how one is expected to call the constructor (and any other methods) in our docs, right?

      yours,
      Michael

        I usually take 'ref $class || $class' to allow me to use an extant object to call the constructor instead of uaing a class name.

        Sure. I've used and written such myself. I wrote such code only because I was following the examples given me in the docs. No other reason.

        The point is Why? Why do you let users of your package use objects to call constructors? Why do you want to be able to write $obj->new vs Obj->new? What is the benefit of this more complicated and slower code?

Re5: Constructor/Factory Orthodoxy
by dragonchild (Archbishop) on Feb 26, 2003 at 15:03 UTC
    Cloning is a separate balliwack from from constructors. That's the crux of why I'm saying that $class = ref $class || $class; is a bad construct. It's saying that this method can be called either as Foo->new() or $foo->new(). That shouldn't be allowed. I would prefer to see die "new() is a class method!" if ref $class; instead.

    And, yes, you should say (ref $foo)->new(); or, better, my $classname = ref $foo; $classname->new();. (Though, I don't know why you'd ever want to do that. Anytime I've ever seen that, I always saw a restructuring that would improve the code and remove that construct.)

    ------
    We are the carpenters and bricklayers of the Information Age.

    Don't go borrowing trouble. For programmers, this means Worry only about what you need to implement.

      I find it almost comical that this one construct gives so many people angina. I agree that cloning is separate and different. To clone means to make identical. To make a new instance of something means just that: make a new one. So in my mind you can do both. Offer a clone method if you truly are going to make an exact copy of the referent; offer a new method that allows instances as referents if you want to allow someone to make a new one like them. In other words, I don't see the dual constructor as two distinct methods (new and clone) but rather as two ways of asking for a new instance. I don't see the great value in forcing the caller to create the class name each time. In my mind new one of this class and new one like this one I already have are basically the same thing that this one simple line in one place can deal with. I believe the only reason people get so uptight about it is that traditional OO languages don't allow or offer it. If we follow that line of thinking we also should require set names for constructors, no inheritence of constructors, and (according to most) no multiple inheritence.

        "new" means to make a completely new one. Cloning doesn't mean to make a new one. It means copy this one. The big difference is when the object has a list of other objects contained within it. What does clone mean in this case? Does A and B both point to the same list (shallow copy)? Does B have its own list, each with clones of those contained objects (deep copy)? It's not clear, and that's because a given application might want either behavior, or something completely different. When something isn't clear, it should be made clear. Making it clear, in my mind, involves different methods. Maybe they share a bunch of stuff under the hood, but that's a good thing. There should be different presentations for different actions, though.

        "That line", as you put it, removes the distinction between the two concepts. That's not so good. Also, if you asked most people who used it, they would have no idea why that line was there, what it was good for, and what behaviors it allowed/disallowed. That is what my angina is about. If you write a line, you should know what that line does.

        ------
        We are the carpenters and bricklayers of the Information Age.

        Don't go borrowing trouble. For programmers, this means Worry only about what you need to implement.

      That shouldn't be allowed.

      Yes, but this is Perl. You know. More ways to do things. Do What I Mean. Stuff like that. The mentality of "it shouldn't be allowed if it's not the way I like it" doesn't belong in the Perl world. You find that overthere ----> http://www.python.org.

      Abigail

        "That shouldn't be allowed" is a personal statement. I'm not on p5p. I don't submit RFC's. I pontificate. :-)

        ------
        We are the carpenters and bricklayers of the Information Age.

        Don't go borrowing trouble. For programmers, this means Worry only about what you need to implement.