There are two hard problems in computer science: cache invalidation and naming things*. Questions about naming things, for me, show up the most when designing a new class (and I wrap nearly all my code into a class, these days). Since I used to earn my rent by writing code in C++, my method names in Perl tend to reflect that heritage.

The most obvious example of this pattern is the way I name object constructors. If I have a package, Xyzzy, the constructor for that class is usually called Xyzzy::new. When the initialization of an object is expensive, I would wrap the constructor in a singleton design pattern, and call that method new. A simplified implementation of this constructor might look like the following:

package Xyzzy; { # scope of cache my $cached = {}; sub new { my $class = shift; # may be a derived subclass my @args = @_; # reuse singleton object iff a valid one is available. delete $cached->{$class} if ($class->cache_invalid()); return $cached->{$class} if ($cached->{$class}); my $obj = {}; # no object found in cache, bless( $obj, $class ); # so create a new one. $obj->_initialize( @args ); # expensive! $cached->{$class} = $obj; # cache data by class id return $obj; } } # scope of cache Ö other methods here Ö

This design pattern allowed me to conceal the singleton nature of a Xyzzy, and I used to think that was a good thing.

Recently, however, the needs of my job called for me to write a substantial quantity of code in the Programming Language That Shall Not Be Named. That language was written with a philosophy that directly opposes TMTOWTDI Ė for any task you want to perform, there is One True Way you must do it. It is a philosophy that complicates the implementation of simple one liners, but greatly reduces, I suspect, the time spent grading test questions which must be answered by writing code in that language.

One of the True Way conflicts I encountered while working in this programming language was the implementation of singleton constructors. You cannot choose a different name for your constructor, and the memory allocation of the object is done externally before your constructor code gets invoked. In short, there is simply no way you can override the constructor with a singleton allocator, and any class method that implements a singleton design pattern must be explicitly invoked by the caller. This leaves me with something like

package Xyzzy; { # scope of cache my $cached = {}; sub instance { my $class = shift; # may be a derived subclass my @args = @_; # reuse singleton object iff a valid one is available. delete $cached->{$class} if ($class->cache_invalid()); return $cached->{$class} if ($cached->{$class}); # no object in the cache, so create/return a new one. my $obj = new $class( @args ); $cached->{$class} = $obj; return $obj; } } # scope of cache sub new { my $class = shift; my @args = @_; my $obj = {}; bless( $obj, $class ); $obj->_initialize( @args ); # expensive! return $obj; }

Having switched to this new nomenclature, I find that singleton instances are only reused when I want them to be used. True, thatís almost always, but this naming technique does leave me the option of constructing a new object instance if I wanted to do something ugly to it and didnít want to risk polluting the cache. On the other hand, if I want to use this pattern on a class that is already widely used, I have to go on a global search-and-destroy mission, replacing constructor calls with calls to instance(), if I want to benefit from the performance improvement that comes from using a singleton.

These days, I still find myself banging my head on the desk when none of the four different ways I might solve a problem in Perl can be applied in The Other Language, but I think this one particular technique is beginning to grow on me. And Iím glad Perl follows TMTOWTDI; it allows me to bring back these new techniques back into my regular job, and benefit from them here, as well.

*Also, off-by-one errors. But if I had said that up above, someone may have accused me of being swayed by that Other Programming Language into parroting the Spanish Inquisition sketch Ė and that is a dead parrot.

Replies are listed 'Best First'.
Re: Singleton Design Patterns and Naming Things
by holli (Abbot) on Jan 25, 2018 at 19:11 UTC
    I could write a lenghty comment now, or I could just be lazy and link to somebody who says it better than I ever could. Guess what, I am lazy.


    You can lead your users to water, but alas, you cannot drown them.

      ++ (I loved watching someone say stuff I've been thinking & saying for years; but more clearly, concisely and well, just better, than I ever have.)

      With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
      Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
      "Science is about questioning the status quo. Questioning authority". The enemy of (IT) success is complexity.
      In the absence of evidence, opinion is indistinguishable from prejudice. Suck that fhit
      Thank you, yes. Kevlin Henney could easily become a massive time-sink.
Re: Singleton Design Patterns and Naming Things
by QM (Parson) on Jan 25, 2018 at 13:52 UTC
    I can guess at the Programming Language That Shall Not Be Named, but that doesn't really matter.

    After learning several other languages, and running into TIMTOWTDI, I realized that Pascal should be "TMBOWTDI" (aka "There Might Be One Way To Do It"). I suspect you're not talking about Pascal.

    I liked Pascal, as a learning language, for it's explicit structure. In school, if a program compiled, you were nearly done. With Programming Language That Shall Not Be Named, a successful compile means almost nothing.

    Quantum Mechanics: The dreams stuff is made of

      There are, alas, many programming languages may fit the anti-TMTOWTDI paradigm. You may have a different language in mind when I say Language That Shall Not Be Named; the one I was actually thinking of is interpreted, not compiled.

      I learned Pascal in 1983, and at the time it was a huge improvement over BASIC -- I could write structured (read: non-spaghetti) code in a human-readable language that did most of what I wanted (the key exception being that I couldn't take the address of a variable on the stack). Learning Pascal made me a better programmer.

      I cast off Pascal for C, however, finding it more powerful and more empowering -- and when my work in C led me to X11 and Xt, a whole new world of object-oriented programming was opened up to me. I don't recommend either of those languages today, but knowing them improved my code. The biggest changes to my programming since then were triggered by C++ and Perl5 -- both languages that allowed me to use built-in features to do things I already wanted to do, but easier. Hash-indexed variables, I'm looking at you: thank you.

      Other languages I have worked with over the years (including SysV shell, FORTRAN, Lisp, and Python2) haven't ever caught my fancy like Perl did, but each in its own way colored my experience in ways that have improved my Perl code.