View on GitHub

Developer-Knowledge-Base

Техническая автобиография и попытка систематизировать знания 📚

Одиночка (Singleton)

Паттерн Одиночка гарантирует, что класс имеет только один экземпляр, и предоставляет глобальную точку доступа к этому экзепляру.

UML

Статья на Википедии

Абстрактная реализация на C# (GoF)

    /// <summary>
    /// MainApp startup class for Structural
    /// Singleton Design Pattern.
    /// </summary>
    internal class MainApp
    {
        /// <summary>
        /// Entry point into console application.
        /// </summary>
        private static void Main()
        {
            // Constructor is protected -- cannot use new
            Singleton s1 = Singleton.Instance();
            Singleton s2 = Singleton.Instance();

            // Test for same instance
            if (s1 == s2)
            {
                Console.WriteLine("Objects are the same instance");
            }

            // Wait for user
            Console.ReadKey();
        }
    }

    /// <summary>
    /// The 'Singleton' class
    /// </summary>
    internal class Singleton
    {
        private static Singleton instance;

        // Constructor is 'protected'
        protected Singleton()
        {
        }

        public static Singleton Instance()
        {
            // Uses lazy initialization.
            // Note: this is not thread safe.
            if (instance == null)
            {
                instance = new Singleton();
            }

            return instance;
        }
    }

Реальная реализация на C# (GoF)

    /// <summary>
    /// MainApp startup class for Real-World
    /// Singleton Design Pattern.
    /// </summary>
    internal class MainApp
    {
        /// <summary>
        /// Entry point into console application.
        /// </summary>
        private static void Main()
        {
            LoadBalancer b1 = LoadBalancer.GetLoadBalancer();
            LoadBalancer b2 = LoadBalancer.GetLoadBalancer();
            LoadBalancer b3 = LoadBalancer.GetLoadBalancer();
            LoadBalancer b4 = LoadBalancer.GetLoadBalancer();

            // Same instance?
            if (b1 == b2 && b2 == b3 && b3 == b4)
            {
                Console.WriteLine("Same instance\n");
            }

            // Load balance 15 server requests
            LoadBalancer balancer = LoadBalancer.GetLoadBalancer();
            for (int i = 0; i < 15; i++)
            {
                string server = balancer.Server;
                Console.WriteLine("Dispatch Request to: " + server);
            }

            // Wait for user
            Console.ReadKey();
        }
    }

    /// <summary>
    /// The 'Singleton' class
    /// </summary>
    internal class LoadBalancer
    {
        private static LoadBalancer instance;
        private List<string> servers = new List<string>();
        private Random random = new Random();

        // Lock synchronization object
        private static object locker = new object();

        // Constructor (protected)
        protected LoadBalancer()
        {
            // List of available servers
            servers.Add("ServerI");
            servers.Add("ServerII");
            servers.Add("ServerIII");
            servers.Add("ServerIV");
            servers.Add("ServerV");
        }

        public static LoadBalancer GetLoadBalancer()
        {
            // Support multithreaded applications through
            // 'Double checked locking' pattern which (once
            // the instance exists) avoids locking each
            // time the method is invoked
            if (instance == null)
            {
                lock (locker)
                {
                    if (instance == null)
                    {
                        instance = new LoadBalancer();
                    }
                }
            }

            return instance;
        }

        // Simple, but effective random load balancer
        public string Server
        {
            get
            {
                int r = random.Next(servers.Count);
                return servers[r].ToString();
            }
        }
    }

Улучшенная реальная реализация на C# (GoF)

    /// <summary>
    /// MainApp startup class for .NET optimized
    /// Singleton Design Pattern.
    /// </summary>
    internal class MainApp
    {
        /// <summary>
        /// Entry point into console application.
        /// </summary>
        private static void Main()
        {
            var b1 = LoadBalancer.GetLoadBalancer();
            var b2 = LoadBalancer.GetLoadBalancer();
            var b3 = LoadBalancer.GetLoadBalancer();
            var b4 = LoadBalancer.GetLoadBalancer();

            // Confirm these are the same instance
            if (b1 == b2 && b2 == b3 && b3 == b4)
            {
                Console.WriteLine("Same instance\n");
            }

            // Next, load balance 15 requests for a server
            var balancer = LoadBalancer.GetLoadBalancer();
            for (int i = 0; i < 15; i++)
            {
                string serverName = balancer.NextServer.Name;
                Console.WriteLine("Dispatch request to: " + serverName);
            }

            // Wait for user
            Console.ReadKey();
        }
    }

    /// <summary>
    /// The 'Singleton' class
    /// </summary>
    internal sealed class LoadBalancer
    {
        // Static members are 'eagerly initialized', that is,
        // immediately when class is loaded for the first time.
        // .NET guarantees thread safety for static initialization
        private static readonly LoadBalancer instance = new LoadBalancer();

        // Type-safe generic list of servers
        private List<Server> servers;

        private Random random = new Random();

        // Note: constructor is 'private'
        private LoadBalancer()
        {
            // Load list of available servers
            servers = new List<Server>
                {
                  new Server{ Name = "ServerI", IP = "120.14.220.18" },
                  new Server{ Name = "ServerII", IP = "120.14.220.19" },
                  new Server{ Name = "ServerIII", IP = "120.14.220.20" },
                  new Server{ Name = "ServerIV", IP = "120.14.220.21" },
                  new Server{ Name = "ServerV", IP = "120.14.220.22" },
                };
        }

        public static LoadBalancer GetLoadBalancer()
        {
            return instance;
        }

        // Simple, but effective load balancer
        public Server NextServer
        {
            get
            {
                int r = random.Next(servers.Count);
                return servers[r];
            }
        }
    }

    /// <summary>
    /// Represents a server machine
    /// </summary>
    internal class Server
    {
        // Gets or sets server name
        public string Name { get; set; }

        // Gets or sets server IP address
        public string IP { get; set; }
    }

Реализация на C# (Head First)

    internal class SingletonClient
    {
        private static void Main(string[] args)
        {
            var singleton = Singleton.getInstance();
            singleton.SaySomething();

            // .NET singleton threadsafe example.

            var es1 = EagerSingleton.GetInstance();
            var es2 = EagerSingleton.GetInstance();
            var es3 = EagerSingleton.GetInstance();

            if (es1 == es2 && es2 == es3)
            {
                Console.WriteLine("Same instance");
            }

            // Wait for user
            Console.ReadKey();
        }
    }

    #region Singleton

    public class Singleton
    {
        private static Singleton _uniqueInstance;
        private static readonly object _syncLock = new Object();

        // other useful instance variables here

        private Singleton()
        {
        }

        public static Singleton getInstance()
        {
            // Lock entire body of method
            lock (_syncLock)
            {
                if (_uniqueInstance == null)
                {
                    _uniqueInstance = new Singleton();
                }
                return _uniqueInstance;
            }
        }

        // other useful methods here
        public void SaySomething()
        {
            Console.WriteLine("I run, therefore I am");
        }
    }

    internal sealed class EagerSingleton
    {
        // CLR eagerly initializes static member when class is first used
        // CLR guarantees thread safety for static initialisation
        private static readonly EagerSingleton _instance = new EagerSingleton();

        // Note: constructor is private
        private EagerSingleton()
        {
        }

        public static EagerSingleton GetInstance()
        {
            return _instance;
        }
    }

    #endregion Singleton

Реализация на JAVA

    /**
     * Singleton class. Eagerly initialized static instance guarantees thread safety.
     */
    public final class EagerlyInitializedSingleton {

        private static final EagerlyInitializedSingleton INSTANCE = new EagerlyInitializedSingleton();

        private EagerlyInitializedSingleton() {}

        public static EagerlyInitializedSingleton getInstance() {
            return INSTANCE;
        }
    }

    /**
     * The Initialize-on-demand-holder idiom is a secure way of creating a lazy initialized singleton
     * object in Java.
     *
     * The technique is as lazy as possible and works in all known versions of Java. It takes advantage
     * of language guarantees about class initialization, and will therefore work correctly in all
     * Java-compliant compilers and virtual machines.
     *
     * The inner class is referenced no earlier (and therefore loaded no earlier by the class loader) than
     * the moment that getInstance() is called. Thus, this solution is thread-safe without requiring special
     * language constructs (i.e. volatile or synchronized).
     */
    public final class InitializingOnDemandSingleton {

        private InitializingOnDemandSingleton() {}

        public static InitializingOnDemandSingleton getInstance() {
            return HelperHolder.INSTANCE;
        }

        private static class HelperHolder {
            private static final InitializingOnDemandSingleton INSTANCE =
                new InitializingOnDemandSingleton();
        }
    }

    /**
     * Thread-safe Singleton class.
     *
     * The instance is lazily initialized and thus needs synchronization mechanism.
     *
     * Note: if created by reflection then a singleton will not be created but multiple options in the same classloader.
     */
    public final class ThreadSafeLazyLoadedSingleton {

        private static ThreadSafeLazyLoadedSingleton instance;

        private ThreadSafeLazyLoadedSingleton() {}

        public static synchronized ThreadSafeLazyLoadedSingleton getInstance() {
            if (instance == null) {
                instance = new ThreadSafeLazyLoadedSingleton();
            }
            return instance;
        }
    }

     /**
      * Double check locking
      *
      * http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html
      *
      * Broken under Java 1.4.
      */
     public final class ThreadSafeDoubleCheckLockingSingleton {

        private static volatile ThreadSafeDoubleCheckLockingSingleton instance;

        private ThreadSafeDoubleCheckLockingSingleton() {
            // to prevent instantiating by Reflection call
            if (instance != null) {
                throw new IllegalStateException("Already initialized.");
            }
        }

        public static ThreadSafeDoubleCheckLockingSingleton getInstance() {
            // local variable increases performance by 25 percent
            // Joshua Bloch "Effective Java, Second Edition", p. 283-284

            ThreadSafeDoubleCheckLockingSingleton result = instance;
            // Check if singleton instance is initialized. If it is initialized then we can return the instance.
            if (result == null) {
                // It is not initialized but we cannot be sure because some other thread might have initialized it
                // in the meanwhile. So to make sure we need to lock on an object to get mutual exclusion.
                synchronized (ThreadSafeDoubleCheckLockingSingleton.class) {
                    // Again assign the instance to local variable to check if it was initialized by some other thread
                    // while current thread was blocked to enter the locked zone. If it was initialized then we can
                    // return the previously created instance just like the previous null check.
                    result = instance;
                    if (result == null) {
                        // The instance is still not initialized so we can safely (no other thread can enter this zone)
                        // create an instance and make it our singleton instance.
                        instance = result = new ThreadSafeDoubleCheckLockingSingleton();
                    }
                }
            }
            return result;
        }
    }

    /**
     * Enum based singleton implementation.
     *
     * Effective Java 2nd Edition (Joshua Bloch) p. 18
     */
    public enum EnumSingleton {

        INSTANCE;

        @Override
        public String toString() {
            return getDeclaringClass().getCanonicalName() + "@" + hashCode();
        }
    }