Beefy Boxes and Bandwidth Generously Provided by pair Networks
Do you know where your variables are?
 
PerlMonks  

Caching a default instance to avoid initializing it every time (with Moo)

by Dallaylaen (Hermit)
on Feb 08, 2018 at 14:19 UTC ( #1208712=perlquestion: print w/replies, xml ) Need Help??
Dallaylaen has asked for the wisdom of the Perl Monks concerning the following question:

Hello dear esteemed monks,

I have a class that is read-only. It does have parameters, but for the most cases a default instance would fit. So I'd like to avoid unneeded initialization and just return $ONE_AND_TRUE_INSTANCE if such exists and no parameters are given to new().

Here's how I would've done this outside Moo/Moose:

my $ONE_TRUE_INSTANCE = __PACKAGE__->new; sub new { my ($class, %args) = @_; return $ONE_TRUE_INSTANCE if ($ONE_TRUE_INSTANCE and $class eq __PACKAGE__ and !%args); # normal constructor here ... };

Can the same be achieved with Moo? How can my example code be improved? Maybe there's a proper architectural solution and/or pattern for this?

Thank you,

UPDATE: actually I can do this with Moo, so the question remains - what can be fixed in architecture to avoid the need for "defaulton":

package Defaulton; use Moo; has foo => is => ro => default => sub { 42 }; my $INST = __PACKAGE__->new; around new => sub { my ($orig, $class, @args) = @_; return $INST if $INST and $class eq __PACKAGE__ and !@args; return $class->$orig(@args); }; 1;

Replies are listed 'Best First'.
Re: Caching a default instance to avoid initializing it every time (with Moo)
by Eily (Prior) on Feb 08, 2018 at 15:09 UTC

    It kinda looks like a singleton, except not exactly so I don't know if Moo can do this. But maybe since what you want is neither of the two behaviours you might expect for a class (always a new instance, or always the same), this can be done in a separate function, rather than the constructor?

    my $default; sub get_instance { my ($class, %args) = @_; return $default ||= $class->new unless %args; return $class->new(%args); }

      "...looks like a singleton...I don't know if Moo can do this..."

      Probably this can be easily done with Role::Singleton by just saying with 'Role::Singleton';?

      I never used this and stumbled over it by chance.

      Best regards, Karl

      «The Crux of the Biscuit is the Apostrophe»

      perl -MCrypt::CBC -E 'say Crypt::CBC->new(-key=>'kgb',-cipher=>"Blowfish")->decrypt_hex($ENV{KARL});'Help

        You'd still have to write a wrapper if you want a single interface to sometimes provide a new instance, sometimes the singleton. But I like that this interface leaves the choice to the caller, you can either have a new instance, regardless of input, or the singleton when you know it can work.

      I ended up going that way because around new wasn't welcome by the team. However, from my point of view caching a read-only object should be transparent to the user of the class as long as it's documented and there's a way to really instantiate a new object if one wants to.

Re: Caching a default instance to avoid initializing it every time (with Moo)
by Your Mother (Bishop) on Feb 08, 2018 at 17:19 UTC

    I tend to do what Eily showed but there is also MooX::ClassAttribute

    BEGIN { package Catty; use Moo; use MooX::ClassAttribute; class_has "catcat" => is => "lazy", clearer => 1; # Sometimes desirable, like cache clearing. sub _build_catcat { my @them = qw( Abyssinian Balinese Chartreux Korat Nebelung Somali Tonkinese ); $them[rand@them]; } 1; } use Catty; my $catty = Catty->new; print $catty->catcat, $/; my $fat_catty = Catty->new; print $fat_catty->catcat, $/; Catty->clear_catcat; print $catty->catcat, $/; print $fat_catty->catcat, $/;

    As for your question about design: a cached copy of something that is global to a process/env is fine. You can set it up to be readonly or have a private writer too for testing mock objects or something. You'd probably need to give details about what the design is and does to get solid advice on whether or not your case might have other, possibly better, options.

Re: Caching a default instance to avoid initializing it every time (with Moo)
by Anonymous Monk on Feb 08, 2018 at 18:42 UTC
    This is a Singleton pattern, and that is how you should implement it. For instance, let there be a sub instance which either returns a cached copy or creates a new instance and caches it. Don't try to do this in a constructor or a wrapper around it – you will confuse other people, at best. Give them what they are conditioned to expect to see. You also need to be very sure that you are storing the cached instance in a persistent global variable as you think you are.
      Give them what they are conditioned to expect to see.

      Throwing a colander of words spaghetti at the wall and managing to have a piece stick is perhaps a valid approach to problem solving but so is doing blockchain in pencil and paper.

      You also need to be very sure that you are storing the cached instance in a persistent global variable as you think you are.

      Where is your example code to test this stern advice? If it was worth saying, it's worth showing.

      It is insulting that, again, there were three working, technical answers presented before your reply and you still felt the need to chime in, adding nothing. Then again, maybe you can't read the code in the three examples so you might not have realized the question was trebly answered already.

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://1208712]
Approved by marto
help
Chatterbox?
and nobody stirs...

How do I use this? | Other CB clients
Other Users?
Others lurking in the Monastery: (5)
As of 2018-06-23 03:09 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    Should cpanminus be part of the standard Perl release?



    Results (125 votes). Check out past polls.

    Notices?