On design patterns: When should I use the singleton?

后端 未结 20 2992
失恋的感觉
失恋的感觉 2020-11-22 02:45

The glorified global variable - becomes a gloried global class. Some say breaking object-oriented design.

Give me scenarios, other than the good old logger where it

相关标签:
20条回答
  • 2020-11-22 03:18

    Read only singletons storing some global state (user language, help filepath, application path) are reasonable. Be carefull of using singletons to control business logic - single almost always ends up being multiple

    0 讨论(0)
  • 2020-11-22 03:18

    A singleton should be used when managing access to a resource which is shared by the entire application, and it would be destructive to potentially have multiple instances of the same class. Making sure that access to shared resources thread safe is one very good example of where this kind of pattern can be vital.

    When using Singletons, you should make sure that you're not accidentally concealing dependencies. Ideally, the singletons (like most static variables in an application) be set up during the execution of your initialization code for the application (static void Main() for C# executables, static void main() for java executables) and then passed in to all other classes that are instantiated which require it. This helps you maintain testability.

    0 讨论(0)
  • 2020-11-22 03:22

    It can be very pragmatic to configure specific infrastructure concerns as singletons or global variables. My favourite example of this is Dependency Injection frameworks that make use of singletons to act as a connection point to the framework.

    In this case you are taking a dependency on the infrastructure to simplify using the library and avoid unneeded complexity.

    0 讨论(0)
  • 2020-11-22 03:24

    A Singleton candidate must satisfy three requirements:

    • controls concurrent access to a shared resource.
    • access to the resource will be requested from multiple, disparate parts of the system.
    • there can be only one object.

    If your proposed Singleton has only one or two of these requirements, a redesign is almost always the correct option.

    For example, a printer spooler is unlikely to be called from more than one place (the Print menu), so you can use mutexes to solve the concurrent access problem.

    A simple logger is the most obvious example of a possibly-valid Singleton, but this can change with more complex logging schemes.

    0 讨论(0)
  • 2020-11-22 03:26

    First of all, let's distinguish between Single Object and Singleton. The latter is one of many possible implementations of the former. And the Single Object's problems are different from Singleton's problems. Single Objects are not inherently bad and sometimes are the only way to do things. In short:

    • Single Object - I need just one instance of an object in a program
    • Singleton - create a class with a static field. Add a static method returning this field. Lazily instantiate a field on the first call. Always return the same object.
    public class Singleton {
        private static Singleton instance;
    
        private Singleton() {}
    
        public static Singleton instance() {
            if (instance == null) {
                instance = new Singleton();
            }
            return instance;
        }
    }
    

    As you can see, "Singleton" pattern in its canonical form is not very testing-friendly. This can be easily fixed, though: just make the Singleton implement an interface. Let's call it "Testable Singleton" :)

    public class Singleton implements ISingleton {
        private static Singleton instance;
    
        private Singleton() {}
    
        public static ISingleton instance() {
            if (instance == null) {
                instance = new Singleton();
            }
            return instance;
        }
    }
    

    Now we can mock Singleton because we use it via the interface. One of the claims is gone. Let's see if we can get rid of another claim - shared global state.

    If we strip Singleton pattern down, at its core it's about lazy initialization:

    public static ISingleton instance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
    

    That's the whole reason for it to exist. And that's the Single Object pattern. We take it away and put to the factory method, for instance:

    public class SingletonFactory {
        private static ISingleton instance;
    
        // Knock-knock. Single Object here
        public static ISingleton simpleSingleton() {
            if (instance == null) {
                instance = new Singleton();
            }
            return instance;
        }
    }
    

    What's the difference with our Testable Singleton? There is none, because this is the essense of the Single Object pattern - it doesn't matter whether you implement it as a Singleton or a Factory Method or a Service Locator. You still have some shared global state. This can become a problem if it's accessed from multiple threads. You will have to make simpleSingleton() synchronized and cope with all the multithreading issues.

    One more time: whatever approach you choose, you will have to pay the Single Object price. Using a Dependency Injection container just shifts the complexity to the framework which will have to cope with Single Object's inherent issues.

    Recap:

    1. Most of people who mention Singleton mean Single Object
    2. One of the popular ways to implement it is the Singleton pattern
    3. It has its flaws that can be mitigated
    4. However, the most of Singleton's complexity roots in Single Object's complexity
    5. Regardless of how you instantiate your Single Object, it's still there, be it a Service Locator, a Factory Method or something else
    6. You can shift the complexity to a DI container which is (hopefully) well-tested
    7. Sometimes using the DI container is cumbersome - imagine injecting a LOGGER to every class
    0 讨论(0)
  • 2020-11-22 03:28

    As everyone has said, a shared resource - specifically something that cannot handle concurrent access.

    One specific example that I have seen, is a Lucene Search Index Writer.

    0 讨论(0)
提交回复
热议问题