Multiton: Difference between revisions
m (→{{header|Raku}}: simplify) |
(Added Python) |
||
(4 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. |
||
< |
<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) |
||
</ |
</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.. |
||
<!--< |
<!--<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> |
||
<!--</ |
<!--</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 |
<syntaxhighlight lang="raku" line># 20211001 Raku programming solution |
||
enum MultitonType < Gold Silver Bronze >; |
enum MultitonType < Gold Silver Bronze >; |
||
Line 133: | Line 227: | ||
class Multiton { |
class Multiton { |
||
my %instances = MultitonType.keys Z=> $ ⚛= 1 xx |
my %instances = MultitonType.keys Z=> $ ⚛= 1 xx * ; |
||
has $.type is rw; |
has $.type is rw; |
||
method TWEAK { |
method TWEAK { $.type = 'Nothing' unless cas(%instances{$.type}, 1, 0) } |
||
} |
} |
||
Line 148: | Line 242: | ||
} |
} |
||
); |
); |
||
}</ |
}</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. |
||
< |
<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</ |
var m3 = Multiton.getInstance(3) // produces an error</syntaxhighlight> |
||
{{out}} |
{{out}} |
Latest revision as of 20:44, 3 March 2024
- 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
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
#!/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
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)