0
\$\begingroup\$

In one of my java projects I use singletons a lot and I was looking for a way to reduce repeating code. I made a SingletonUtils class that accepts a class and supplier parameter. Then stores the instance in a hash map. This way the static instance isn't stored as a static variable in the class I want to make a singleton.

My question is, is there any problems/cons with implementing the singleton pattern like this and is there an easier way to do this or is this the best way?

public class SingletonUtils {
    public static HashMap<Class, Object> instances = new HashMap<>();
    public static <T> T getInstance(final Class<T> singletonObj, final Supplier<T> creator) {
        if(!instances.containsKey(singletonObj)) {
            if(creator == null) return null;
            else instances.put(singletonObj, creator.get());
        }
        return (T) instances.get(singletonObj);
    }

    public static boolean instanceExists(final Class singletonObj) {
        return instances.containsKey(singletonObj);
    }

    public static void removeInstance(final Class singletonObj) {
        instances.remove(singletonObj);
    }
}

public final class AuthManager {
    private AuthManager() { }

    public static AuthManager getNewInstance() {
        return SingletonUtils.getInstance(AuthManager.class, AuthManager::new);
    }
}
\$\endgroup\$

2 Answers 2

6
\$\begingroup\$

Let me just express the first thing that came to mind when I read this question's title:

Not another Singleton question.

Let's ignore in the following that Singletons are an anti-pattern in Object-Oriented Programming.

Look. I get it. Singletons are nice and easy.
Everything about them makes things easier to think about.
The whole point of a singleton is that only one instance of it ever exists.
It's by definition the single source of truth.

What your code does is subtly, but completely undermine that assumption.

Consider the following sequence:

AuthManager authSingleton = AuthManager.getNewInstance();
SingletonUtils.removeInstance(AuthManager.class),
AuthManager anotherSingleton = AuthManager.getNewInstance();

Those don't need to be in the same thread, the same class or the same anything. As long as they run in the same JVM, authSingleton == anotherSingleton will be false. And this is a complete and utter breakage of the semantics of a Singleton.

What you tried to do with that class is not something that you want. You're centrally controlling the lifetime of singletons. But you're not correctly enforcing the semantics of these singletons.

This code is something like an ObjectPool. Implying that it has anything to do with singletons is incorrect though.

Do not make singletons complicated:

public final class AuthManager {
    private static final AuthManager INSTANCE = new AuthManager();
    public static AuthManager get() {
        return INSTANCE;
    }

    private AuthManager() {
    }
}

^^ This is a complete, threadsafe and lazily initialized Singleton. In 99.9999% of the use-cases of a singleton, this is enough. But 90% of all Singleton uses are just flat out wrong.

There is no useful reason for an "AuthManager" to be a Singleton. Don't cargo-cult.

\$\endgroup\$
1
  • 1
    \$\begingroup\$ This helped a lot, thanks, I will be changing the AuthManager to not be a singleton. Before now I didn't think of the case where the system may need to use multiple databases for the auth system and thus need multiple instances of the AuthManager. I'm going to rethink all my singleton usage in the future as you are right I probably don't need them in most situations. \$\endgroup\$
    – JWCompDev
    Commented Nov 14, 2018 at 13:02
2
\$\begingroup\$

I agree with Vogel612 - Singleton is an anti-pattern because of nomerous reasons. Just don't.

However I want to mention the pattern "ServiceLocator" because it is very similar to your question. Maybe you want to read about it. But - just like the Singleton - the ServiceLocator is widely known as an anti-pattern.

\$\endgroup\$
1
  • 4
    \$\begingroup\$ If both are known as anti-patterns, isn't it trading one problem for another? \$\endgroup\$
    – Mast
    Commented Nov 14, 2018 at 13:46

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.