oop advanced

How to create a Moose singleton class in Perl?

Question

How to create a Moose singleton class in Perl?

Creating a singleton class in Perl with Moose involves restricting a class so that only one instance of it can ever be created. The singleton pattern ensures that all code using the class shares the same object, which is useful for managing shared resources or global state in an object-oriented way.

While Moose itself does not provide built-in support for singletons, you can easily implement this pattern by controlling object construction in your class and caching the single instance internally. Below is a thorough explanation and an idiomatic example demonstrating how to write a Moose singleton.

How It Works

  • Intercept construction: Override the BUILDARGS or new method to control object instantiation.
  • Store the instance: Keep a private class-level variable holding the singleton object.
  • Return the same instance: On subsequent calls to new, return the cached object instead of creating a new one.

This approach respects Moose’s normal construction and attribute initialization mechanisms while enforcing a single instance.

Example: Moose Singleton Class

use strict;
use warnings;
use feature 'say';

package MySingleton;
use Moose;

# Private class variable to hold the singleton instance
my $instance;

has 'value' => (
    is  => 'rw',
    isa => 'Str',
    default => 'default',
);

# Override new to implement the singleton pattern
around BUILDARGS => sub {
    my ($orig, $class, @args) = @_;

    # Only instantiate once
    if (defined $instance) {
        # Return the existing instance's internal data (hashref)
        # So Moose can build the object from this instead of creating a new one
        # But actually, to return the same instance, override 'new' instead
        return $instance->BUILDARGS(@args);
    }

    # First time creation: call original BUILDARGS
    return $class->$orig(@args);
};

around 'new' => sub {
    my ($orig, $class, @args) = @_;

    # Return existing instance if it exists
    return $instance if defined $instance;

    # Otherwise create and cache the instance
    $instance = $class->$orig(@args);
    return $instance;
};

sub DEMOLISH {
    # Prevent instance destruction to keep singleton alive (optional)
    # Or leave it empty to allow normal cleanup
}

package main;

# Demonstration of singleton behavior
my $obj1 = MySingleton->new(value => 'foo');
say "Object 1 value: " . $obj1->value;

my $obj2 = MySingleton->new(value => 'bar'); # Attempt to create new instance

say "Object 2 value: " . $obj2->value;

if ($obj1 == $obj2) {
    say "obj1 and obj2 are the same instance (singleton works)";
} else {
    say "Different instances (singleton failed)";
}

# Modify the singleton via one reference
$obj1->value('changed');

say "Object 2 value after change: " . $obj2->value;

Explanation

  • my $instance; declares a lexical variable scoped to the package to cache the singleton object.
  • The around 'new' method override intercepts all calls to new. If an instance already exists, it returns that instead of creating a new one.
  • around BUILDARGS keeps Moose’s parameter normalization intact, ensuring attributes can be passed as usual.
  • Because the same object is returned every time, you ensure shared state.
  • The demo script proves the singleton works by showing that two "new" calls return the same object and observe changes.

Common Gotchas

  • Thread Safety: This singleton is not thread-safe. For multithreaded environments, you’d need locking around instance creation.
  • Subclassing: If subclassed, each subclass would share the same cached instance unless you adjust the code to cache per class name.
  • DESTROY and Cleanup: The singleton is kept alive by the lexical variable $instance. If your program needs explicit cleanup, customize DEMOLISH carefully.
  • Using MooseX::Singleton: There is a CPAN extension MooseX::Singleton that does this for you automatically, but if using core Moose alone, this pattern works well.

Perl and Moose Concepts

The example illustrates a few Perl and Moose concepts:

  • Moose adds powerful object orientation to Perl with attributes (declared using has) and method modifiers like around.
  • Method modifiers allow you to wrap existing methods to customize behavior without rewriting.
  • sigils like $ capture scalar data; here $instance holds the singleton object.
  • TMTOWTDI: Perl’s “There’s more than one way to do it” philosophy lets you choose the singleton implementation style that fits your needs.

This code is fully runnable on any Perl 5 with Moose installed (no external modules beyond Moose). It prints to STDOUT demonstrating the singleton behavior.

Verified Code

Executed in a sandbox to capture real output. • v5.34.1 • 137ms

Tip: edit code and use “Run (Browser)”. Server runs always execute the published, verified snippet.
STDOUT
Object 1 value: foo
Object 2 value: foo
obj1 and obj2 are the same instance (singleton works)
Object 2 value after change: changed
STDERR
(empty)

Was this helpful?

Related Questions