【问题标题】:How to design a Singleton that can be instantiated only by 1 class如何设计一个只能由 1 个类实例化的单例
【发布时间】:2010-10-21 01:33:04
【问题描述】:

我想设计一个类似于单例的类,该类只有一个主实例,但也可以有多个主实例的克隆。只允许 1 个类创建主实例,其他所有人都可以创建一个克隆。像这样的东西(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 应该是唯一允许实例化单例的类。在 C++ 中,这可以通过完全隐藏创建逻辑并将 MyClass 作为 Singleton 的朋友来实现。

【问题讨论】:

  • 什么是单例的克隆?克隆时相同的对象(通过引用)还是具有相同状态的新对象?如果第一个,你的问题是自我冗余的,如果第二个,不要称它为单例......原型可能吗?

标签: c# singleton


【解决方案1】:

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

这两种方法都使用了工厂概念;由于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();
    }
}

【讨论】:

  • 感谢您的解决方案。虽然我最终没有使用这个解决方案,但间接分配技巧让我想到了另一种设计解决方案。好主意,谢谢!
【解决方案2】:

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

像这样:

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

     public static Singleton MainInstance
     {
         return _mainInstance;
     }
}

您的唯一实例将存储在类的 _mainInstance 静态字段中,并且不可能创建任何其他实例。

【讨论】:

  • 我在方法和属性中添加了“静态”。我确实了解如何写一个signleton,那不是最初的问题。我希望整个库中只有一个类能够实例化第一个实例。
【解决方案3】:

您可以使用内部构造函数使类只能由其程序集中的其他类实例化:

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 方法的存在似乎无法使其成为一体。

【讨论】:

    【解决方案4】:
    【解决方案5】:

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

    您的单例实现几乎是正确的,只是 _mainInstanceMainInstance 需要为 static

    【讨论】:

    • 我添加了“静态”,这是我的错误。克隆方法与原始问题无关 - 如何使单例的第一个实例由解决方案中的 1 和 1 唯一类创建。所以只有一个入口点 - 您要么通过负责创建唯一实例的 MainClass 获取此实例,要么获取 null。
    • 你可以让Singleton成为Main的私有内部类,让Singleton实现一个公开可见的接口,随处可见
    • 但是你的问题没有多大意义。如果 Singleton 每次都返回相同的实例,为什么只允许您的 MainClass 调用它?你害怕发生什么?
    • 因为主类负责处置它,所以它应该“拥有”它(在松散的意义上)。将获得克隆的所有其他消费者都将获得它以进行短暂/临时使用并立即处置它。
    【解决方案6】:

    类似Monostate Pattern?尽管所有实例都共享相同的状态,所以这可能不是您想要的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-10-13
      • 1970-01-01
      • 2013-05-24
      • 1970-01-01
      相关资源
      最近更新 更多