tags:

views:

123

answers:

6

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.

+2  A: 

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.

Andrew Bezzub
I added 'static' to the method and property. I do understand how to write a signleton, thats not the original question. I want only one class in the whole library to be able to instantiate the first instance.
kateroh
A: 

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.

codymanix
I added 'static', that was a mistake on my part. Clone method has nothing to do with the original question - how to make this first instance of the singleton be created by 1 and 1 only class in the solution. So there is only one point of entry - you either get this instance through the MainClass that will be responsibly for creating the one and only instance or you get null.
kateroh
You can make Singleton a private inner class of Main and let Singleton implement a public visible interface which is visible everywhere
codymanix
But you question does not make much sense. If Singleton everytime returns the same instance why only your MainClass should be allowed to call it? What do you fear to happen?
codymanix
Because the main class is responsible for desposing it thats why it should "own" it (in a loose sense). Every other consumer that will get a clone will get it for a brief/temp usage and immediately dispose it.
kateroh
+2  A: 

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();
    }
}
Dr. Wily's Apprentice
Thanks for the solution. Though I ended up not using this solution, the indirect assigment trick made me think of another design solution. Great ideas, thanks!
kateroh
+1  A: 

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.

Jordão
A: 

Something like the Monostate Pattern? Though the instances all share the same state, so this might not quite what you are looking for.

Helper Method