How to design a Singleton that can be instantiated only by 1 class
I want to design a class that will be like a singleton in the way that there will be a single main instance of that class, but there can also be multiple clones of the main instance. Only 1 class will be allowed to create the main instance and everybody else can create a clone. Something like this (c#):
class Singleton
{
private static Singleton _mainInstance;
private Singleton() {..}
public void Clone() {..}
public static Singleton MainInstance
{
if (_mainInstance == null)
{
_mainInstance = new Singleton开发者_开发百科(); // how to secure this for only 1 class?
}
return _mainInstance;
}
}
class MainClass
{
public MainClass()
{
Singleton.MainInstance ....
}
}
MainClass should be the only class that is allowed to instantiate the singleton. In C++ this could be accomplished by hiding the creation logic completely and having MyClass as a friend of Singleton.
Below is a complete working implementation that demonstrates two possible approaches to accomplishing what you want.
Both approaches utilize a Factory concept; since the Singleton
's constructor is private, only the nested Factory
class is able to create new instances of the Singleton
class. Since the nested Factory
class itself is private, the only way to get an instance of a factory is through either the Singleton.GetFactoryFirstOneWins
method ("First one wins" approach) or the Singleton.AssignFactories
method ("Indirect assignment" approach).
interface IFactory<T>
{
T CreateInstance();
}
class Singleton
{
class Factory : IFactory<Singleton>
{
public Singleton CreateInstance()
{
// return a clone of _MainInstance
return new Singleton(_MainInstance);
}
}
// *** Begin "First one wins" approach
static IFactory<Singleton> _FactoryFirstOneWins;
public static IFactory<Singleton> GetFactoryFirstOneWins()
{
if (_FactoryFirstOneWins != null)
throw new InvalidOperationException("A factory has already been created.");
return _FactoryFirstOneWins = new Factory();
}
// *** End "First one wins" approach
// *** Begin "Indirect assignment" approach
public static void AssignFactories()
{
MainClass.SingletonFactory = new Factory();
}
// *** End "Indirect assignment" approach
private static readonly Singleton _MainInstance = new Singleton();
public static Singleton MainInstance
{
get { return _MainInstance; }
}
private Singleton()
{
// perform initialization logic
this.SomeValue = 5; // pick some arbitrary number
}
private Singleton(Singleton instance)
{
// perform cloning logic here to make "this" a clone of "instance"
this.SomeValue = instance.SomeValue;
}
public int SomeValue { get; set; }
public void DoSomething()
{
Console.WriteLine("Singleton.DoSomething: " + this.SomeValue);
// ...
}
}
class MainClass
{
private static IFactory<Singleton> _SingletonFactory;
public static IFactory<Singleton> SingletonFactory
{
get { return _SingletonFactory; }
set { _SingletonFactory = value; }
}
public Singleton Singleton { get; private set; }
public MainClass()
{
this.Singleton = SingletonFactory.CreateInstance();
}
public void DoWork()
{
Console.WriteLine("MainClass.DoWork");
this.Singleton.DoSomething();
// ...
}
}
class Program
{
static void Main(string[] args)
{
// you could either use the "First one wins" approach
MainClass.SingletonFactory = Singleton.GetFactoryFirstOneWins();
// or use the "Indirect assignment" approach
Singleton.AssignFactories();
// create two separate MainClass instances
MainClass mc1 = new MainClass();
MainClass mc2 = new MainClass();
// show that each one utilizes a Singleton cloned from Singleton.MainInstance
mc1.DoWork();
mc2.DoWork();
// updating mc1.Singleton.SomeValue does not affect any other instances of MainClass
mc1.Singleton.SomeValue = 7;
mc1.DoWork();
mc2.DoWork();
// updating Singleton.MainInstance.SomeValue affects any new instances of MainClass, but not existing instances
Singleton.MainInstance.SomeValue = 10;
MainClass mc3 = new MainClass();
mc1.DoWork();
mc2.DoWork();
mc3.DoWork();
}
}
You can simply use Singleton pattern with private static field inside your class.
Like this:
class Singleton
{
private static Singleton _mainInstance = new Singleton();
private Singleton() { }
public void Clone() {..}
public static Singleton MainInstance
{
return _mainInstance;
}
}
You will have your only instance stored inside _mainInstance static field of your class and it would be impossible to create any other instances.
You can use an internal constructor to make a class instantiatable only by other classes in its assembly:
class InternalInstantiation {
internal InternalInstantiation() {}
public void Clone() {}
}
class MainClass {
private InternalInstantiation _instance =
new InternalInstantiation();
}
Also, you can nest a class inside another to instantiate a class with a private constructor.
class PrivateInstantiation {
private PrivateInstantiation() { }
public void Clone() {}
public class MainClass {
private PrivateInstantiation _instance =
new PrivateInstantiation();
}
}
You can also create a scheme which the private instantiatable class injects an instance of it in the main class (no other class can use it):
public class MainClass {
internal PrivateInstantiation PrivateInstantiation { get; set; }
public MainClass() {
PrivateInstantiation.CreateAndSet(this);
}
}
class PrivateInstantiation {
private PrivateInstantiation() { }
public void Clone() {}
public static void CreateAndSet(MainClass mc) {
mc.PrivateInstantiation = new PrivateInstantiation();
}
}
Note that I didn't call your class a singleton because the presence of a Clone
method doesn't seem to make it one.
Let's try it: you need prototype pattern, not singleton.
The PROTOTYPE PATTERN comes under the classification of Creational Patterns. The creational patterns deals with the best way to create objects. This helps to copy or clone the existing objects to create new ones rather than creating from the scratch.
The singleton pattern means there there is only one instance of that class. And if your class has some kind of Clone methode then everybody is able to clone this instance. So I cannot see your problem.
Your singleton implementation is almost correct, only that _mainInstance
and MainInstance
need to be static
.
Something like the Monostate Pattern? Though the instances all share the same state, so this might not quite what you are looking for.
精彩评论