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

With all due respect, my initial question has to do with learning about the 'why' of the Orthodox approach. I feel like you have just parroted the orthodoxy right back to me. I am not taking a stand on right vs. wrong, here, but am attempting to learn with good examples and counter-examples.

First off, don't do my $class = ref $class || $class; That is poor style. Ignore the fact that 99% of the known world does it that way. It's nearly always wrong.

So why, other than your opinion, are these things wrong?

I would use a Factory specifically. First off, you keep the knowledge of which classes actually exist in one place. I like manifests. It helps us maintenance programmers immensely. Also, as a maintenance programmer, I don't like dual-use functions. They are confusing. Base classes are meant to be abstract. Factories are meant to churn stuff out. Factories and Base classes aren't the same thing.

The example I gave above keeps the knowledge of which classes actually exist in one place (or, rather, it's certainly a convenient place to keep the knowledge of classes but doesn't mandate that the knowledge be kept there). That was part of my illustration. Your concerns about dual-use functions are fine -- though they don't bother me personally I'm just as happy with separate constructor methods and factory methods. I'm interested in hearing more about why you think they should reside in separate classes, however. I also wonder if you could elaborate on why you think Base classes must be abstract. (or do you mean that you should always start with an interface -- which Perl is not necessarily admirably equipped to handle?)

Just cause you can doesn't mean you should.

This teaches me nothing, but it does repeat the orthodox party line.

Thanks for the response,
Matt

Replies are listed 'Best First'.
Re3: Constructor/Factory Orthodoxy
by dragonchild (Archbishop) on Feb 26, 2003 at 01:05 UTC
    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:

    • Documentation that's relevant
    • Access to the original programmers
    • Understanding of what you were doing
    • Time to do it right
    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.

      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.

        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.