Multiton: Difference between revisions

From Rosetta Code
Content added Content deleted
m (→‎{{header|Raku}}: simplify)
(Added Python)
 
(3 intermediate revisions by 3 users not shown)
Line 17: Line 17:
=={{header|Julia}}==
=={{header|Julia}}==
A registry (just in memory, not on disk) is used below instead of an enum. The registry is protected by a lock on the Multiton constructor, to prevent two threads creating the same object at the same time.
A registry (just in memory, not on disk) is used below instead of an enum. The registry is protected by a lock on the Multiton constructor, to prevent two threads creating the same object at the same time.
<lang julia>struct Multiton{T}
<syntaxhighlight lang="julia">struct Multiton{T}
data::T
data::T
function Multiton(registry, refnum, data)
function Multiton(registry, refnum, data)
Line 64: Line 64:
# produce error
# produce error
m5 = Multiton(reg, 5)
m5 = Multiton(reg, 5)
</lang>{{out}}
</syntaxhighlight>{{out}}
<pre>
<pre>
Multiton is Multiton{String}("zero")
Multiton is Multiton{String}("zero")
Line 72: Line 72:
Multiton is Multiton{Float64}(1.0)
Multiton is Multiton{Float64}(1.0)
ERROR: LoadError: Cannot find a Multiton in registry with instance reference number 5
ERROR: LoadError: Cannot find a Multiton in registry with instance reference number 5
</pre>

=={{header|Perl}}==
<syntaxhighlight lang="perl"># 20211215 Perl programming solution

use strict;
use warnings;

BEGIN {
package MultitonDemo ;
use Moo;
with 'Role::Multiton';
has [qw(attribute)] => ( is => 'rw');
$INC{"MultitonDemo.pm"} = 1;
}

use MultitonDemo;

print "We create several instances and compare them to see if multiton is in effect.\n";
print "\n";
print "Instance Constructor Attribute\n";
print "\n";
print "0 multiton 0\n";
print "1 multiton 1\n";
print "2 multiton 0\n";
print "3 new 0\n";
print "4 new 0\n";

my $inst0 = MultitonDemo->multiton (attribute => 0);
my $inst1 = MultitonDemo->multiton (attribute => 1);
my $inst2 = MultitonDemo->multiton (attribute => 0);
my $inst3 = MultitonDemo->new (attribute => 0);
my $inst4 = MultitonDemo->new (attribute => 0);

print "\n";
if ($inst0 eq $inst1) { print "Instance0 and Instance1 share the same object\n" };
if ($inst1 eq $inst2) { print "Instance1 and Instance2 share the same object\n" };
if ($inst0 eq $inst2) { print "Instance0 and Instance2 share the same object\n" };
if ($inst0 eq $inst3) { print "Instance0 and Instance3 share the same object\n" };
if ($inst3 eq $inst4) { print "Instance3 and Instance4 share the same object\n" };</syntaxhighlight>
{{out}}
<pre>We create several instances and compare them to see if multiton is in effect.

Instance Constructor Attribute

0 multiton 0
1 multiton 1
2 multiton 0
3 new 0
4 new 0

Instance0 and Instance2 share the same object
</pre>
</pre>


Line 81: Line 133:
Put this (up to "end class") in a separate file, to keep allowed and instances private. Classes are not [yet] supported under pwa/p2js.<br>
Put this (up to "end class") in a separate file, to keep allowed and instances private. Classes are not [yet] supported under pwa/p2js.<br>
Technically I suppose this should really use new_dict()/getd()/setd(), but I'm quite sure you'll cope somehow..
Technically I suppose this should really use new_dict()/getd()/setd(), but I'm quite sure you'll cope somehow..
<!--<lang Phix>(notonline)-->
<!--<syntaxhighlight lang="phix">(notonline)-->
<span style="color: #008080;">without</span> <span style="color: #008080;">javascript_semantics</span>
<span style="color: #008080;">without</span> <span style="color: #008080;">javascript_semantics</span>
<span style="color: #004080;">sequence</span> <span style="color: #000000;">allowed</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{</span><span style="color: #008000;">"zero"</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"one"</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"two"</span><span style="color: #0000FF;">},</span>
<span style="color: #004080;">sequence</span> <span style="color: #000000;">allowed</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{</span><span style="color: #008000;">"zero"</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"one"</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"two"</span><span style="color: #0000FF;">},</span>
Line 114: Line 166:
<span style="color: #0000FF;">?</span><span style="color: #000000;">e</span><span style="color: #0000FF;">.</span><span style="color: #000000;">id</span>
<span style="color: #0000FF;">?</span><span style="color: #000000;">e</span><span style="color: #0000FF;">.</span><span style="color: #000000;">id</span>
<span style="color: #0000FF;">?</span><span style="color: #000000;">f</span><span style="color: #0000FF;">.</span><span style="color: #000000;">id</span>
<span style="color: #0000FF;">?</span><span style="color: #000000;">f</span><span style="color: #0000FF;">.</span><span style="color: #000000;">id</span>
<!--</lang>-->
<!--</syntaxhighlight>-->
{{out}}
{{out}}
<pre>
<pre>
Line 124: Line 176:
"two_544"
"two_544"
</pre>
</pre>

=={{header|Python}}==
{{works with|Python|3.x}}
<syntaxhighlight lang="python">#!/usr/bin/python

import threading

class Multiton:
def __init__(self, registry, refnum, data=None):
with registry.lock:
if 0 < refnum <= registry.max_instances and registry.instances[refnum] is None:
self.data = data
registry.instances[refnum] = self
elif data is None and 0 < refnum <= registry.max_instances and isinstance(registry.instances[refnum], Multiton):
self.data = registry.instances[refnum].data
else:
raise Exception("Cannot create or find instance with instance reference number {}".format(refnum))

class Registry:
def __init__(self, maxnum):
self.lock = threading.Lock()
self.max_instances = maxnum
self.instances = [None] * maxnum

reg = Registry(3)
m0 = Multiton(reg, 1, "zero")
m1 = Multiton(reg, 2, 1.0)
m2 = Multiton(reg, 1)
m3 = Multiton(reg, 2)

for m in [m0, m1, m2, m3]:
print("Multiton is {}".format(m.data))


# produce error
#m2 = Multiton(reg, 3, [2])

# produce error
# m3 = Multiton(reg, 4, "three")

# produce error
# m5 = Multiton(reg, 5)</syntaxhighlight>


=={{header|Raku}}==
=={{header|Raku}}==
Tried to translate [https://wikipedia.org/wiki/Multiton_pattern#Implementations the C# example] at WP but not sure if my interpretation/implementation is correct
Tried to translate [https://wikipedia.org/wiki/Multiton_pattern#Implementations the C# example] at WP but not sure if my interpretation/implementation is correct
<lang perl6># 20211001 Raku programming solution
<syntaxhighlight lang="raku" line># 20211001 Raku programming solution


enum MultitonType < Gold Silver Bronze >;
enum MultitonType < Gold Silver Bronze >;
Line 148: Line 242:
}
}
);
);
}</lang>
}</syntaxhighlight>
{{out}}
{{out}}
<pre>
<pre>
Line 168: Line 262:


Although all Wren code runs within the context of a fiber (of which there can be thousands) only one fiber can run at a time and so the language is effectively single threaded. Thread-safety is therefore never an issue.
Although all Wren code runs within the context of a fiber (of which there can be thousands) only one fiber can run at a time and so the language is effectively single threaded. Thread-safety is therefore never an issue.
<lang ecmascript>import "/dynamic" for Enum
<syntaxhighlight lang="wren">import "./dynamic" for Enum


var MultitonType = Enum.create("MultitonType", ["zero", "one", "two"])
var MultitonType = Enum.create("MultitonType", ["zero", "one", "two"])
Line 200: Line 294:
System.print(m2)
System.print(m2)


var m3 = Multiton.getInstance(3) // produces an error</lang>
var m3 = Multiton.getInstance(3) // produces an error</syntaxhighlight>


{{out}}
{{out}}

Latest revision as of 20:44, 3 March 2024

Multiton is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.
Description

The multiton pattern is a design pattern which generalizes the singleton pattern. Whereas the singleton allows only one instance of a class to be created, the multiton pattern allows for the controlled creation of multiple instances, which it manages through the use of a map.


Task

Implement a basic Multiton class or other structure and test that it works as intended. If your language does not support the object oriented paradigm, then try to emulate a multiton as best you can with the tools available.

If your language supports multithreading, then you may optionally implement a thread safe variant as well.


Related task




Julia

A registry (just in memory, not on disk) is used below instead of an enum. The registry is protected by a lock on the Multiton constructor, to prevent two threads creating the same object at the same time.

struct Multiton{T}
    data::T
    function Multiton(registry, refnum, data)
        lock(registry.spinlock)
        if 0 < refnum <= registry.max_instances && registry.instances[refnum] isa Nothing
            multiton = new{typeof(data)}(data)
            registry.instances[refnum] = multiton
            unlock(registry.spinlock)
            return multiton
        else
            unlock(registry.spinlock)
            error("Cannot create instance with instance reference number $refnum")
        end
    end
    function Multiton(registry, refnum)
        if 0 < refnum <= registry.max_instances && registry.instances[refnum] isa Multiton
            return registry.instances[refnum]
        else
            error("Cannot find a Multiton in registry with instance reference number $refnum")
        end
    end
end

struct Registry
        spinlock::Threads.SpinLock
        max_instances::Int
        instances::Vector{Union{Nothing, Multiton}}
        Registry(maxnum) = new(Threads.SpinLock(), maxnum, fill(nothing, maxnum))
end

reg = Registry(3)
m0 = Multiton(reg, 1, "zero")
m1 = Multiton(reg, 2, 1.0)
m2 = Multiton(reg, 3, [2])
m3 = Multiton(reg, 1)
m4 = Multiton(reg, 2)


for m in [m0, m1, m2, m3, m4]
   println("Multiton is $m")
end

# produce error
# m3 = Multiton(reg, 4, "three")

# produce error
m5 = Multiton(reg, 5)
Output:
Multiton is Multiton{String}("zero")
Multiton is Multiton{Float64}(1.0)
Multiton is Multiton{Vector{Int64}}([2])
Multiton is Multiton{String}("zero")
Multiton is Multiton{Float64}(1.0)  
ERROR: LoadError: Cannot find a Multiton in registry with instance reference number 5

Perl

# 20211215 Perl programming solution

use strict;
use warnings;

BEGIN {
   package MultitonDemo ;
   use Moo;
   with 'Role::Multiton';
   has [qw(attribute)] => ( is => 'rw');
   $INC{"MultitonDemo.pm"} = 1;
}

use MultitonDemo;

print "We create several instances and compare them to see if multiton is in effect.\n";
print "\n";
print "Instance   Constructor  Attribute\n";   
print "\n";
print "0            multiton       0\n";
print "1            multiton       1\n";
print "2            multiton       0\n";
print "3              new          0\n";
print "4              new          0\n";

my $inst0 = MultitonDemo->multiton (attribute => 0); 
my $inst1 = MultitonDemo->multiton (attribute => 1);
my $inst2 = MultitonDemo->multiton (attribute => 0); 
my $inst3 = MultitonDemo->new      (attribute => 0);
my $inst4 = MultitonDemo->new      (attribute => 0);

print "\n";
if ($inst0 eq $inst1) { print "Instance0 and Instance1 share the same object\n" };
if ($inst1 eq $inst2) { print "Instance1 and Instance2 share the same object\n" }; 
if ($inst0 eq $inst2) { print "Instance0 and Instance2 share the same object\n" }; 
if ($inst0 eq $inst3) { print "Instance0 and Instance3 share the same object\n" }; 
if ($inst3 eq $inst4) { print "Instance3 and Instance4 share the same object\n" };
Output:
We create several instances and compare them to see if multiton is in effect.

Instance   Constructor  Attribute

0            multiton       0
1            multiton       1
2            multiton       0
3              new          0
4              new          0

Instance0 and Instance2 share the same object

Phix

Library: Phix/Class

No attempt is made for thread safety, since multiple threads accessing these would need their own locking anyway, not that it is difficult to invoke enter_cs() and leave_cs().
I thought about adding a get_multiton() function to avoid calling new() all the time, but it would (probably/almost certainly) just invoke new() itself anyway.
Put this (up to "end class") in a separate file, to keep allowed and instances private. Classes are not [yet] supported under pwa/p2js.
Technically I suppose this should really use new_dict()/getd()/setd(), but I'm quite sure you'll cope somehow..

without javascript_semantics
sequence allowed = {"zero","one","two"},
         instances = {NULL,NULL,NULL}

public class Multiton
    public string id
    function Multiton(string id)
        integer k = find(id,allowed)
        if k=0 then crash("not allowed") end if
        if instances[k] = NULL then
            this.id = id&sprintf("_%d",rand(999))
            instances[k] = this
        end if
        return instances[k]
    end function
end class

Multiton a = new({"zero"}),
         b = new({"one"}),
         c = new({"two"}),
         d = new({"zero"}),
--       e = new(),         -- crashes
--       f = new({"three"}) -- crashes
         e = new({"one"}),
         f = new({"two"})

?a.id
?b.id
?c.id
?d.id
?e.id
?f.id
Output:
"zero_651"
"one_111"
"two_544"
"zero_651"
"one_111"
"two_544"

Python

Works with: Python version 3.x
#!/usr/bin/python

import threading

class Multiton:
    def __init__(self, registry, refnum, data=None):
        with registry.lock:
            if 0 < refnum <= registry.max_instances and registry.instances[refnum] is None:
                self.data = data
                registry.instances[refnum] = self
            elif data is None and 0 < refnum <= registry.max_instances and isinstance(registry.instances[refnum], Multiton):
                self.data = registry.instances[refnum].data
            else:
                raise Exception("Cannot create or find instance with instance reference number {}".format(refnum))

class Registry:
    def __init__(self, maxnum):
        self.lock = threading.Lock()
        self.max_instances = maxnum
        self.instances = [None] * maxnum

reg = Registry(3)
m0 = Multiton(reg, 1, "zero")
m1 = Multiton(reg, 2, 1.0)
m2 = Multiton(reg, 1)
m3 = Multiton(reg, 2)

for m in [m0, m1, m2, m3]:
   print("Multiton is {}".format(m.data))


# produce error
#m2 = Multiton(reg, 3, [2])

# produce error
# m3 = Multiton(reg, 4, "three")

# produce error
# m5 = Multiton(reg, 5)

Raku

Tried to translate the C# example at WP but not sure if my interpretation/implementation is correct

# 20211001 Raku programming solution 

enum MultitonType < Gold Silver Bronze >;

class Multiton { 

   my %instances = MultitonType.keys Z=> $ ⚛= 1 xx * ;

   has $.type is rw; 

   method TWEAK { $.type = 'Nothing' unless cas(%instances{$.type}, 1, 0) }
}

race for ^10 -> $i {
   Thread.start(
      sub {
#         sleep roll(^2);
         my $obj = Multiton.new: type => MultitonType.roll;
         say "Thread ", $i, " has got ", $obj.type;
      }
   );
}
Output:
Thread 5 has got Bronze
Thread 9 has got Gold
Thread 7 has got Nothing
Thread 8 has got Nothing
Thread 3 has got Nothing
Thread 2 has got Nothing
Thread 1 has got Nothing
Thread 0 has got Silver
Thread 4 has got Nothing
Thread 6 has got Nothing

Wren

Library: Wren-dynamic

This more or less follows the lines of the C# example in the linked Wikipedia article.

Although all Wren code runs within the context of a fiber (of which there can be thousands) only one fiber can run at a time and so the language is effectively single threaded. Thread-safety is therefore never an issue.

import "./dynamic" for Enum

var MultitonType = Enum.create("MultitonType", ["zero", "one", "two"])

class Multiton {
    // private constructor
    construct new_(type) {
        _type = type
    }

    static getInstance(type) {
        if (!(0...MultitonType.members.count).contains(type)) {
            Fiber.abort("Invalid MultitonType member.")
        }
        if (!__instances) __instances = {}
        if (!__instances.containsKey(type)) __instances[type] = new_(type)
        return __instances[type]
    }

    type { _type }

    toString { MultitonType.members[_type] }
}

var m0 = Multiton.getInstance(MultitonType.zero)
var m1 = Multiton.getInstance(MultitonType.one)
var m2 = Multiton.getInstance(MultitonType.two)

System.print(m0)
System.print(m1)
System.print(m2)

var m3 = Multiton.getInstance(3)  // produces an error
Output:
zero
one
two
Invalid MultitonType member.
[./multiton line 13] in getInstance(_)
[./multiton line 33] in (script)