web-dev-qa-db-fra.com

Comment puis-je mettre en œuvre une classe Abstract Singleton en Java?

Voici mon échantillon abstrait Singleton Classe:

public abstract class A {
    protected static A instance;
    public static A getInstance() {
        return instance;
    }
    //...rest of my abstract methods...
}

Et voici la mise en œuvre concrète:

public class B extends A {
    private B() { }
    static {
        instance = new B();
    }
    //...implementations of my abstract methods...
}

Malheureusement, je ne peux pas obtenir le code statique dans la classe B d'exécuter, de sorte que la variable d'instance ne peut jamais être définie. J'ai essayé ceci:

Class c = B.class;
A.getInstance() - returns null;

et ça

ClassLoader.getSystemClassLoader().loadClass("B");
A.getInstance() - return null;

Les deux dans le débogueur Eclipse, le code statique n'est jamais exécuté. La seule façon de trouver pour que le code statique exécuté est de modifier l'accessibilité sur le constructeur de B au public et de l'appeler.

J'utilise Sun-Java6-Jre sur Ubuntu 32bit pour exécuter ces tests.

24
Simon

Abstrait singleton? Ne sonne pas viable pour moi. Le motif singleton nécessite un constructeur private et cela rend déjà le sous-classement impossible. Vous devrez repenser votre conception. Le modèle d'usine abstrait peut être plus approprié pour le but particulier.

22
BalusC

Vous essayez d'obtenir une classe abstraite jouer deux rôles très différents:

  • le rôle d'abstraction d'usine pour un service (singleton) pouvant avoir plusieurs implémentations substituables,
  • le rôle d'interface de service,

et en plus de cela, vous voulez aussi que le service soit singleton et appliquer "singletoness" sur toute la famille de classes, pour une raison quelconque, vous ne tenez pas en train de mettre en cache les instances de service.

Quelqu'un (je le ferais) dira que cela sent très mal, pour de nombreuses raisons pourraient enfreindre la séparation des préoccupations, les singletons rendent des tests unités impossibles ", etc.

Quelqu'un d'autre dira que c'est OK-ISH, il n'a pas besoin de nombreuses infrastructures différentes et a une sorte d'interface fluide-ish que vous voyez dans une tierce partie très courante (héritage) Java API .

La mauvaise partie exige que les enfants choisissent la mise en œuvre de la méthode de la méthode d'usine parent. Cette responsabilité devrait être poussée et centralisée dans la superclasse abstraite. Sinon, vous mélangez des modèles combinés ensemble dans des contextes très différents, l'usine abstraite (parent décidera de la famille de classes clients à obtenir) et la méthode d'usine (usines d'enfants sélectionnez ce que les clients obtiendront).

La méthode d'usine n'est également pas pratiquement possible car vous ne pouvez pas remplacer les méthodes statiques, ni les constructeurs.

Il y a des moyens (laids) pour atteindre votre objectif cependant:

public abstract class A{
    public static A getInstance(...){
      if (...)
         return B.getInstance();
      return C.getInstance();
    }

    public abstract void doSomething();

    public abstract void doSomethingElse();

}

public class B extends A{
    private static B instance=new B();

    private B(){
    }

    public static B getInstance(){
        return instance;
    }

    public void doSomething(){
        ...
    }
    ...
}

//do similarly for class C

Le parent pourrait également utiliser la réflexion, les instances de cache, etc.

Une solution plus rapide de test et d'extension est simplement une séparation standard des préoccupations. Les enfants ne vont plus être singleton en soi, mais vous les emballez dans un paquet interne que vous allez documenter comme "privé" et un parent abstrait public dans un paquet externe gérera la mise en cache ou la mise en commun des instances pour enfants, l'application de l'instanciation. La politique est requise sur ces classes.

8
Benedetto Fiorelli

En plus des problèmes, d'autres ont souligné, avoir le champ instance dans A signifie que vous ne pouvez avoir que n singleton dans l'ensemble de la machine virtuelle VM. Si vous avez aussi:

public class C extends A {
    private C() { }
    static {
        instance = new C();
    }
    //...implementations of my abstract methods...
}

... Ensuite, quelle que soit celle de B ou C _ obtient la dernière fois que la dernière victoire sera chargée et l'instance Singleton de l'autre sera perdue.

C'est juste une mauvaise façon de faire des choses.

3
Matt McHenry

J'ai trouvé une meilleure façon d'utiliser Singleton en classe abstraite, qui utilise une carte statique pour maintenir l'instance de la sous-classe.

public abstract class AbstractSingleton {

    private static Map<String, AbstractSingleton> registryMap = new HashMap<String, AbstractSingleton>();

    AbstractSingleton() throws SingletonException {
        String clazzName = this.getClass().getName();
        if (registryMap.containsKey(clazzName)) {
            throw new SingletonException("Cannot construct instance for class " + clazzName + ", since an instance already exists!");
        } else {
            synchronized (registryMap) {
                if (registryMap.containsKey(clazzName)) {
                    throw new SingletonException("Cannot construct instance for class " + clazzName + ", since an instance already exists!");
                } else {
                    registryMap.put(clazzName, this);
                }
            }
        }
    }

    @SuppressWarnings("unchecked")
    public static <T extends AbstractSingleton> T getInstance(final Class<T> clazz) throws InstantiationException, IllegalAccessException {
        String clazzName = clazz.getName();
        if (!registryMap.containsKey(clazzName)) {
            synchronized (registryMap) {
                if (!registryMap.containsKey(clazzName)) {
                    T instance = clazz.newInstance();
                    return instance;
                }
            }
        }
        return (T) registryMap.get(clazzName);
    }

    public static AbstractSingleton getInstance(final String clazzName)
            throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        if (!registryMap.containsKey(clazzName)) {
            Class<? extends AbstractSingleton> clazz = Class.forName(clazzName).asSubclass(AbstractSingleton.class);
            synchronized (registryMap) {
                if (!registryMap.containsKey(clazzName)) {
                    AbstractSingleton instance = clazz.newInstance();
                    return instance;
                }
            }
        }
        return registryMap.get(clazzName);
    }

    @SuppressWarnings("unchecked")
    public static <T extends AbstractSingleton> T getInstance(final Class<T> clazz, Class<?>[] parameterTypes, Object[] initargs)
            throws SecurityException, NoSuchMethodException, IllegalArgumentException,
            InvocationTargetException, InstantiationException, IllegalAccessException {
        String clazzName = clazz.getName();
        if (!registryMap.containsKey(clazzName)) {
            synchronized (registryMap) {
                if (!registryMap.containsKey(clazzName)) {
                    Constructor<T> constructor = clazz.getConstructor(parameterTypes);
                    T instance = constructor.newInstance(initargs);
                    return instance;
                }
            }
        }
        return (T) registryMap.get(clazzName);
    }

    static class SingletonException extends Exception {
        private static final long serialVersionUID = -8633183690442262445L;

        private SingletonException(String message) {
            super(message);
        }
    }
}

De: https://www.cnblogs.com/wang9192/p/3975748.html

1
Zhou Hongbo