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

The "orthodox party line", as you call it, is the accumulated experiential knowledge of thousands of man-years. It encompasses every single phase of the code development cycle (which, by the way, is based on more of those thousands of man-years).

The complaints I have are from the phase I have the most experience in - maintenance. As a maintenance programmer, I am looking for the code to be as succinct, yet as informative as possible. The key here is that I'd prefer informative over succinct.

Why is that important? It reduces the time I am required to take in order to make the changes I'm told to do safely. The key there is "safely". A maintainer very often will not know the majority of the system. The more information the original developer gives me, the more confidence I have that I know what's going on.

What does all of that matter to the points I made?

  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.)

    new(), by convention, is usually the instantiator. clone(), also by convention, is usually a cloner. new() is a class method, clone() an object method.

    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.

    It's also confusing in that other developers using your class may treat the instantiation method as a cloning method. If that were to happen, that would make my head (as your maintainer) hurt really badly.

  2. Other responders have made the case for why Base classes shouldn't know about their children. I'll just say that dynamic loading of child classes is a maintenance nightmare.

Remember - maintenance programmers will never have the following:

And, just because your system isn't a large system doesn't mean you should code it poorly. Get the right habits now, when you can fix your mistakes. Don't get sloppy.

------
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.

Replies are listed 'Best First'.
Re4: Constructor/Factory Orthodoxy
by mojotoad (Monsignor) on Feb 26, 2003 at 05:13 UTC
    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

      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

      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.

        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