Beefy Boxes and Bandwidth Generously Provided by pair Networks
There's more than one way to do things
 
PerlMonks  

Constructor/Factory Orthodoxy

by mojotoad (Monsignor)
on Feb 25, 2003 at 22:36 UTC ( [id://238593]=perlmeditation: print w/replies, xml ) Need Help??

Update: my phrasing below is rather vaugue as to my intent, I'm afraid. I'm not really asking about separating constructors and factories into separate methods or classes; I'm not asking whether 'new' or any other name is symantically acceptable for these constructs. I am asking about parameter-based subclass generation vs direct instantiation, regardless of how instances are constructed, and more generally about the propogation of defaults through subclasses. Apologies for my obtuseness. Thnx. -- Matt

--- original text below ---

Is there any particular reason why having a constructor serve a dual role as a factory is a bad idea? In other words, if you have a base class with some subclasses, you can either a) instantiate directly from the subclass, or b) have the base class hand you one of the sub classes depending on parameters.

Lately I have found myself wanting to do this very thing, in an attempt to maintain flexibility while at the same time having reasonable defaults, some of which percolate down to subclasses, some of which do not. In other words, if I know ahead of time that I need one of the subclasses, then instantiate directly from that subclass. If I don't care, then instantiate from the base class and let it do what it wants to do by default. Or, dynamically hand the base class a parameter once your program logic dictates which subclass you need (I realize there are any number of ways to load classes dynamically, but this way you can imbed that knowledge/capacity in the classes rather than in your program).

If this is perfectly acceptable, by what name do we call the technique? And in which schools of thought might it provoke howls of rage?

Example:

File Bubba.pm
package Bubba; use strict; use warnings; our %Defaults = ( type => 'Gump', ); sub new { my $this = shift; my $class = ref $this || $this; my %parms = @_; my $type = exists $parms{type} ? $parms{type} : $Defaults{type}; delete $parms{type}; if ($class eq __PACKAGE__) { # if subclasses were in separate modules, we'd # dynamically load here $class .= "::$type"; return $class->new(%parms); } my $self = {}; bless $self, $class; # do stuff with parms as the subclass $self; } sub shout { print "Unobtainable shrimp. No subclass?\n" } package Bubba::Gump; use base 'Bubba'; sub shout { print "Boiled shrimp\n" } package Bubba::Wump; use base 'Bubba'; sub shout { print "Fried shrimp\n" } 1;

File summon_bubba.pl
#!/usr/bin/perl -w use strict; use Bubba; my $b = Bubba->new(); $b->shout; my $g = Bubba->new(type => 'Wump'); $g->shout; my $bg = Bubba::Gump->new(); $bg->shout; my $bw = Bubba::Wump->new(); $bw->shout;

# ./summon_bubba.pl Boiled shrimp Fried shrimp Boiled shrimp Fried shrimp
Cheers,
Matt

Replies are listed 'Best First'.
Re: Constructor/Factory Orthodoxy
by dragonchild (Archbishop) on Feb 25, 2003 at 23:42 UTC
    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.

    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.

    Just cause you can doesn't mean you should.

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

      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

        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.

Re: Constructor/Factory Orthodoxy
by dws (Chancellor) on Feb 25, 2003 at 23:59 UTC
    Is there any particular reason why having a constructor serve a dual role as a factory is a bad idea?

    It's largely a matter of convention and expectation. When I write

    my $frob = new Frob();
    I expect to get a Frob, and future readers of the code might reasonably expect that $frob will hold an instance of Frob. But if I write
    my $frob = Frob::makeFrob();
    there is no such assumption. Nobody is misled. At most, the assumption is that $frob will get a Frob or a subclass of Frob.

    Having new work consistently 99% of the time is a bad thing. It lulls people into traps.

      I'm not here to defend the name "new()" as a factory (I'm not really here to defend anything -- I'm here to learn).

      Symantically, of course, what you say makes sense -- so let's say I call the factory "Bubba::makeBubba". What then of my example? What do you call it, etc? (we can assume that there's also a new() which boostraps from makeBubba() )

      My main question, which I fear was obscured by my reference to a dual-function constructor, concerned this notion of returning subclasses based on parameters vs instantiating the subclasses directly (programmer's choice). In so doing, I was happy to utilize an inheritable constructor as well. (regardless of where the factory method might reside)

      Thanks,
      Matt

        Let's say I call the factory "Bubba::makeBubba". What then of my example? What do you call it, etc?

        I'd pick a name that expresses the intent of the operation. "makeBubba" might be fine. "makeBubbaMeal" might be better. It depends on what your example would look like fully fleshed out.

      When I write my $frob = new Frob (); I expect to get a Frob,

      So do I! What I am more confused about is: Why I should reject a member of Sub_class_of_Frob as not a Frob?

      Too much Perl. Not enough theory or practice with another language to broaden my mind.

        Why I should reject a member of Sub_class_of_Frob as not a Frob?

        Because it violated the expectation I had that

        my $from = new Frob();
        would give me a Frob, not something that is a specialization of a Frob. One can argue about whether new warrants that expectation, but precendent across programming lanaguages does lean strongly that way.

Re: Constructor/Factory Orthodoxy
by simon.proctor (Vicar) on Feb 26, 2003 at 00:48 UTC
    If I have read you right then your base class has foreknowledge of your subclasses. The reason being you can either instantiate the sub class or pass a parameter to the base class and it decides what class to give you.

    Assuming this is correct:

    This would be my reason why you should avoid putting your base class and your factory in the same class. The purpose of a class hierarchy, in my opinion, is that if you were to line them up (single level for simplicity):
    A (base) | B inherits | C inherits
    Then as you go down the chain, you should be adding functionality and your classes become more *specific*.

    Each class should have knowledge of its parent(s) but surely have none of its children? Your structure requires that the base class has knowledge of all classes that inherit from it (and possibly, its deepest descendants <-- though I doubt it).

    I would argue that you need to think deeper regarding why you would want to merge a factory and your base class together. IMHO its better to have a separate factory that has 'knowledge' of the chain in question and can 'do the right thing'.

    Of course, YMMV as it depends on your class hierarchy. I've only done horizontal class factories:
    A | +---+--+---+---+ B C D E <---------- Factory picks b,c,d or e
    Whereas yours looks like:
    A (base) | B -+ | | C |-- Factory can handle the chain (only B and C shown) -+
    But thats another story ;).
      Your structure requires that the base class has knowledge of all classes that inherit from it (and possibly, its deepest descendants <-- though I doubt it).

      The way around this is to have subclasses register with their parent class. Then, the parent consults a registry before instantiation a class, rather than hard-coding knowledge of the class hierarchy.

        Hmmm. I can see the technique but frankly I'd have to see some good uses of it before being convinced. Have you put this concept to use (or at least seen it done well)?

        Given your technique I'd be just as inclined to say you could do the same with a factory class and keep the two apart.

        What is your opinion of the two?
        Why not have the Factory maintain the registry and keep the two separate? The programmer who comes after you will bless you continuously. (If you don't, expect curses.)

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

      Your assumptions were indeed correct (I updated the root node in an attempt to clarify). :)

      Your explanation of why Base classes should know nothing about their subclasses is convincing -- this has a ring of clarity and simplification to it that sounds compelling.

      As others have mentioned elsewhere in this thread, there seems to be room here for a registry or manifest for children classes -- I think this is also an interesting approach that does not bleed knowledge of subclasses into their respective base classes, so long as the interface for the subclasses are the same.

      BTW, the factories I had in mind were "horizontal" as you describe them (Gump and Wump were sibling subclasses of Bubba).

      Thanks for your informative response,
      Matt

      The parent/child knowledge issue sometimes is worth violating IMO -- again in conflict with standard OO. This may not have any bearing on this particular problem but one instance where I found it fits perfectly:

      • I have a set of file classes all derived from a common base class. Each file class represents a file accessed via some different transport (e.g., ftp, http, rsh, ssh).
      • Each file class in turn has a directory class. A directory is just a special type of that file, so it has all the file methods plus some add-ons only directories can do.
      • At construction time, all the caller knows is the file name. It's during construction, when the actual filesystem (or whatever) is consulted that knowledge of a directory arises.
      • If a file does find out it's a directory during construction, it simply morphs itself into its directory subclass by re-blessing itself into that class.

      I like Perl's ability to elegantly address that pattern when it makes sense.

        I had a think about your example and I think that its reasonable to do something like that. I think I'd probably do the same.

        However, I have been trying to think about how to get around your problem without doing this (purely for academic purposes) and the only thing I could think of was a variation on the Decorator pattern? Even then I'm not sure ;)

        Its nice that Perl has this flexibility, I am curious as to how this would be done in a statically typed language such as Java or if you wanted to practise a 'purer' OO approach.

        Again, purely for academic reasons.

        SP
        re-blessing doesn't mean you violate parent-knowledge behavior. In fact, Policy, Manifest, and/or Factory classes would be a better way of doing this.
        1. Create a file object.
        2. The file object, during construction, finds out it's a directory.
        3. The file object will then either:
          • Ask the Factory to for the classname to rebless to
          • Give itself to the Factory and asks to be reblessed
        The factory or policy or manifest should be the one that maintains that knowledge and how to transform.

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

Re: Constructor/Factory Orthodoxy
by hossman (Prior) on Feb 25, 2003 at 23:43 UTC

    I don't think there's anything inheriently wrong with what you describe, back in the day I had lots of "constructors" that were really "factories" (except that I wasn't doing dynamic Module loading ... they were all spelled out at the top).

    I don't find myself writting code like that much anymore ... A big part of that has to do with the general code conventions of where I work now ... but even when I write code for personal projects, I don't think I would use the "Constructor is a Factory" approach -- mainly for clarity.

    Ask yourself what (if anything) you gain by calling your factory method "new", and if it's worth the possible confusion of other people reading your code in the future.

      Ask yourself what (if anything) you gain by calling your factory method "new", and if it's worth the possible confusion of other people reading your code in the future.

      I don't personally gain anything by calling it "new" other than it's typical. I'm just as happy calling it "make_a_big_fat_new_object", or separating constructor methods from factory methods, as I describe above.

      I can certainly understand the concerns with clarity -- there seems to be diverse opinions on that. It wasn't my main concern with the post, however.

      Thanks,
      Matt

Re: Constructor/Factory Orthodoxy
by mojotoad (Monsignor) on Feb 26, 2003 at 22:04 UTC
    A bit of a follow up with some information I've gathered.

    My original question really concerned a concept of "inheritable default properties", but I mangaged to bungle my question. Obviously attribute methods, or mutators, are not the problem here with inheritance -- it's how to cascade defaults from the base class, possibly overriding with new defaults in the subclasses, etc. A related problem, but not quite the same thing. None of the answers above really addressed this, so I flipped through TheDamian's Object Oriented Perl book, learning about some of the constructor/factory issues that are addressed above. (more on that in a bit).

    While doing so, the book refreshed my memory on Tie::SecureHash, which is specifically designed to handle these kinds of issues (and many more) with Perl objects. It's not perfect, but it's great for debugging and tracking problems with attributes and defaults -- then you can load it in 'fast' mode and have it drop back into behaving like a normal hash without the additional overhead. Worth reading up on if you're curious.

    Also interesting were the notions of using a Registry to let the base class (perhaps via a Factory class) to wrangle with configuring the subclasses, rather than encoding knowledge of subclasses within your base class -- as dws suggested.

    As for constructor/factory/cloner -- I was puzzled above by dragonchild's presumption that if new() could also be an object method, rather than a class method, that there was an expectation of it being a cloning method. I had never had this impression and thought he was making a ridiculous assumption. As it turns out, his is more likely to be the traditional assumption in Perldom, rather than mine. I had always assumed that the object version of new() behaved identically to the class method -- that is to say, Foo->new() behaves identically to $foo->new(). For me, $foo->new() was just a convenient way to avoid having to type (ref $foo)->new. Cloning was a completely separate issue, and typically a separate method, for me. So I wondered where dragonchild would get such an assumption.

    Well here's an excerpt on precisely this subject, from TheDamian himself in his book:

    Laziness is a cardinal virtue in Perl, so a custom has developed that when users of a class call a class's constructor as an object method, the defaults the constructor uses are taken from the original object.

    (p. 103)

    and later...

    While many experts view the overloading of constructors as object copiers to be a natural extension of their functionality, others consider the technique unintuitive, too subtle, and more likely to produce obscure and hard-to-maintain code. Whether or not that's the case, it certainly is true that the code for the constructors themselves is considerably more complicated.

    The alternative is to provide a completely separate method for duplicating objects.

    (p. 105)

    So there you have that -- I was certainly mistaken in my notion that there was no such expectation with $foo->new() (and I've been around Perl long enough to have no excuse). Having said that, I'd say I'm firmly in the $foo->clone() camp for copying objects.

    This issue of cloning doesn't really address the differences between a constructor method and a factory method (or class). Some might argue that a constructor is just a special case of a factory. I'd always maintained a "self vs other" outlook on constructors vs factories, but that's certainly not the whole picture. Feel free to read up on them around the web yourself.

    Cheers,
    Matt

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlmeditation [id://238593]
Approved by diotalevi
Front-paged by Enlil
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others making s'mores by the fire in the courtyard of the Monastery: (6)
As of 2024-04-24 05:55 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found