代码之家  ›  专栏  ›  技术社区  ›  kateroh

如何设计只能由一个类实例化的单例

  •  1
  • kateroh  · 技术社区  · 15 年前

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

    6 回复  |  直到 15 年前
        1
  •  2
  •   Dr. Wily's Apprentice    15 年前

    下面是一个完整的工作实现,它演示了实现您所需的两种可能方法。

    两种方法都使用工厂概念;因为 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
  •  2
  •   Andrew Bezzub    15 年前

    您可以简单地将Singleton模式与类内的私有静态字段一起使用。

    这样地:

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

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

        3
  •  1
  •   Jordão    15 年前

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

    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 方法似乎不能使其成为一个整体。

        5
  •  0
  •   codymanix    15 年前

    你的单例实现几乎是正确的,只是 _mainInstance MainInstance static .

        6
  •  0
  •   helpermethod    15 年前

    类似于 Monostate Pattern 是吗?尽管这些实例都共享相同的状态,但这可能不是您所要查找的。