如何设计一个可以仅由1个类实例化的单例

问题描述:

我想设计一个类,它将以类似于单例的方式存在,该类将有一个主实例,但也可能有多个克隆的主要实例。只有1个类可以创建主实例,其他人都可以创建一个克隆。像这样的东西(c#):如何设计一个可以仅由1个类实例化的单例

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应该是唯一允许实例化单例的类。在C++中,可以通过完全隐藏创建逻辑并将MyClass作为Singleton的朋友来完成。

+1

什么是单身人士的克隆?同一对象(通过引用)或克隆时具有相同状态的新对象?如果第一,你的问题是自我多余的,如果第二,不要称之为单身...原型可能? – 2010-10-20 16:59:48

下面是一个完整的工作实现,演示了两种可能的方法来完成你想要的。

这两种方法都使用工厂概念;由于Singleton的构造函数是私有的,因此只有嵌套的Factory类才能创建Singleton类的新实例。由于嵌套Factory类本身是私有的,获得工厂实例的唯一方法是通过Singleton.GetFactoryFirstOneWins方法(“第一个获胜”方法)或Singleton.AssignFactories方法(“间接分配”方法)。

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(); 
    } 
} 
+0

感谢您的解决方案。尽管我最终没有使用这种解决方案,但间接分配技巧让我想到了另一种设计解决方案。好主意,谢谢! – kateroh 2010-10-20 18:12:06

你可以简单地在你的类中使用带有私有静态字段的Singleton模式。

像这样:

class Singleton 
{ 
    private static Singleton _mainInstance = new Singleton(); 
    private Singleton() { } 
    public void Clone() {..} 

    public static Singleton MainInstance 
    { 
     return _mainInstance; 
    } 
} 

你将有你的存储类的_mainInstance静态字段里面你唯一的实例,就不可能创造任何其他实例。

+0

我在方法和属性中添加了'static'。我知道如何写一个signleton,那不是原来的问题。我希望整个库中只有一个类能够实例化第一个实例。 – kateroh 2010-10-20 16:24:21

单例模式意味着只有一个该类的实例。如果你的类有某种克隆方法,那么每个人都可以克隆这个实例。所以我看不到你的问题。

您的单例执行几乎是正确的,只有_mainInstanceMainInstance需要为static

+0

我添加了“静态”,这是我的错误。克隆方法与原始问题无关 - 如何使该单例的第一个实例由解决方案中的1和1类创建。所以只有一个输入点 - 你可以通过MainClass获得这个实例,负责创建一个或者唯一的实例,或者你得到null。 – kateroh 2010-10-20 16:27:36

+0

你可以让Singleton成为Main的一个私有内部类,并让Singleton实现一个公开的可见接口,它在任何地方都可见 – codymanix 2010-10-20 16:41:01

+0

但是你的问题没有多大意义。如果Singleton每次都返回相同的实例,为什么只有你的MainClass应该被允许调用它呢?你害怕发生什么? – codymanix 2010-10-20 16:43:12

您可以使用内部的构造函数只有在其装配其他类使一个类实例化的:

class InternalInstantiation { 
    internal InternalInstantiation() {} 
    public void Clone() {} 
} 

class MainClass { 
    private InternalInstantiation _instance = 
    new InternalInstantiation(); 
} 

此外,您还可以嵌套在另一个内部类实例化一个类有私有构造函数。

class PrivateInstantiation { 
    private PrivateInstantiation() { } 
    public void Clone() {} 

    public class MainClass { 
    private PrivateInstantiation _instance = 
     new PrivateInstantiation(); 
    } 
} 

您还可以创建私有实例化的类中的主类注入它的一个实例(没有其他类可以使用)方案:

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(); 
    } 
} 

请注意,我没有打电话你的班级是一个单身人士,因为Clone方法的出现似乎并没有使它成为一个单身人士。

有点像Monostate Pattern?尽管这些实例都具有相同的状态,所以这可能不太符合您的要求。