2017-10-16 28 views
-1

Je cherche un moyen de mettre en œuvre une classe abstraite (ou effectivement abstraite) qui applique une seule instance de chaque sous-classe. Je suis assez sûr que ce serait assez simple à implémenter avec une Usine, mais je serais intéressé de savoir si cela peut être fait sans connaître tous les types de sous-classes, c'est-à-dire une classe générique d'exécuteur singleton.Comment mettre en œuvre un dérivables Singleton en Java

En ce moment je suis la plupart du temps tout simplement jouer avec l'idée de quelque chose comme ça, donc je ne suis pas à la recherche de commentaires qui remet en question le choix de conception ici.

La langue dans laquelle je travaille est Java, mais pour le moment je ne m'inquiète pas forcément des détails d'implémentation, sauf si ce n'est pas possible en Java, alors, bien sûr, prouver que ce n'est pas possible.

+3

Il n'est pas possible d'appliquer le modèle Singleton sans un constructeur privé, ce qui rend la sous-classe externe impossible. – shmosel

+0

Regardez [ask] et [help/on-topic]. Ce genre de question ouverte n'est pas un bon ajustement. – pvg

+1

'fournir la preuve que ce n'est pas possible' ce n'est pas comme cela que cela fonctionne ... comme dit par @pvg ce n'est pas [aide/sur le sujet] et même si c'était le fardeau de la preuve sur vous. – Oleg

Répondre

0

Je me demande ce que vous essayez de faire. Un couple de possibilités viennent à l'esprit et savoir où cela se dirige pourrait aider.

Option 1

Alors vous pouvez essayer d'utiliser un type enum que votre classe de base abstraite. Chaque constante d'énumération est alors garantie par le langage pour être un singleton. L'énumération peut avoir des méthodes abstraites que les constantes implémentent. Cela fonctionnera mais l'unité de compilation deviendra très grande et difficile à naviguer si vous avez beaucoup de constantes d'implémentation et beaucoup de méthodes abstraites à implémenter. Vous pourriez bien sûr déléguer une partie du travail à des classes auxiliaires si cela commence à devenir incontrôlable.

Option 2

Vous pouvez faire est d'obtenir le constructeur de la classe de base pour vérifier son type réel et le stocker dans un HashSet statique (ou similaire). Si une entrée existe déjà, vous avez deux instances du même singleton. Quelque chose comme

public abstract class BaseClass { 
    private static HashSet<Class<?>> instances = new HashSet<>(); 

    protected BaseClass() { 
     checkInstances(); 
    } 

    private synchronized void checkInstances() { 
     boolean duplicate = instances.add(getClass()); 

     if (duplicate) { 
      throw new RuntimeException("Duplicate class " + getClass().getName()); 
     } 
    } 
} 

L'inconvénient est que l'erreur se produit lors de l'exécution et le code est pas particulièrement jolie et que vous pouvez voir que vous devrez peut-être envisager la synchronisation de l'ensemble

Option 3

Votre dernière option est tout simplement de ne pas demander à la classe de base pour faire appliquer cette restriction. Il devrait probablement le travail des classes dérivées de décider si elles sont singletons ou non. Un constructeur privé dans la classe dérivée est le moyen le plus simple de le faire.

Conclusion

Personnellement, je mettre en œuvre l'option 1 ou l'option 3 que vous ne serez pas les échecs d'exécution.

+0

J'ai trouvé quelque chose de similaire à votre "Option 2" mais je pense que c'est plus encombrant que je ne le souhaiterais, et je ne pense pas que ce serait complètement clair pour quelqu'un qui n'a jamais utilisé quelque chose comme ça auparavant. Donc, je vais probablement recourir à ne pas appliquer Singleton-ness. – jdodle

+0

Je suis d'accord - Option à 2 moche. IMHO faisant appel à singleton-ness devrait être un travail pour la classe singleton elle-même et non pour une classe qu'elle étend. – Stormcloud

0

Tout d'abord, un singleton générique n'a pas de sens.
Une classe parente ne devrait pas être responsable de la récupération et de la gestion des instances de ses sous-classes.
Il crée un fort couplage dans les deux sens (parent-> enfant et enfant-> parent).

En second lieu, comme le dit shmosel, un singleton sous-classement (sans artefact spécial) n'est pas possible.
La clé du modèle singleton est le manque de capacité à instancier la classe en dehors de la classe singleton, donc aucun constructeur public ne doit être fourni.
Dans ces conditions, comment sous-classe la classe singleton? Pour autoriser le sous-classement d'une classe singleton, vous devez avoir un constructeur public tout en vous assurant de ne pas avoir plus d'une instance de la classe.
L'inversion de conteneurs de contrôle tels que le ressort peut le faire (c'est un exemple d'artefact spécial).


Comme une note de côté, je ne considère pas modificateur d'accès peaufinage tels que le modificateur package-private qui pourrait permettre de sous-classe un singleton, mais la limitation est que le singleton serait un singleton seulement en dehors de l'emballage.

+0

Je * pense * qu'il veut écrire un groupe de singletons qui étendent tous une seule classe de base. – Stormcloud

+0

Ce n'est vraiment pas clair comme le dit le PO: "Je cherche un moyen d'implémenter une classe abstraite (ou effectivement abstraite) qui impose une seule instance de chaque sous-classe." J'ai donc conclu que la classe abstraite gère les instances des sous-classes. – davidxxx

+0

Je suis d'accord. Je dois admettre qu'il m'a fallu réfléchir un peu pour savoir quel était le but de cette classe de base. Je suis parti en pensant que c'est la mauvaise classe qui vérifie les singletons. Un constructeur privé dans la classe dérivée le trierait. – Stormcloud

-1

Je voulais dire que les singletons sont mauvais. Mais j'ai trouvé ce problème intéressant, j'ai donc créé ce que vous voulez. Voici le code

public static abstract class SingletonBase { 
     private static HashSet<SingletonBase> instances = new HashSet<>(); 

     { 
      for (SingletonBase sb : instances) { 
       if (sb.getClass() == this.getClass()) throw new RuntimeException("there is already 1 instance"); 
      } 
     } 

     public static <E> E getInstance(Class<E> clazz) { 
      if (!SingletonBase.class.isAssignableFrom(clazz)) { 
       throw new RuntimeException(); 
      } 
      for (SingletonBase sb : instances) { 
       if (sb.getClass() == clazz) return (E) sb; 
      } 

      try { 
       return clazz.newInstance(); 
      } catch (InstantiationException e) { 
       e.printStackTrace(); 
      } catch (IllegalAccessException e) { 
       e.printStackTrace(); 
      } 
      return null; 

     } 

     private SingletonBase() { 
      instances.add(this); 
     } 

    } 
    static class SingletonTest extends SingletonBase{ 

    } 

    static class SecondSingletonTest extends SingletonBase{ 

    } 

    public static void main(String[] args) { 
     for(int i=0;i<=10;i++) 
      System.out.println( SingletonBase.getInstance(SingletonTest.class)); 
     for(int i=0;i<=10;i++) 
      System.out.println( SingletonBase.getInstance(SecondSingletonTest.class)); 
     //throws exception, because we try to create second instance here 
     new SingletonTest(); 
    } 

Il y a quelques problèmes avec l'approche de la création de classes génériques, qui sont résolus ici: d'abord, vous ne pouvez pas créer plus d'une instance, la classe si la base doit garder une trace de toutes les instances, et lorsque vous essayez d'en créer un autre en utilisant new, il va lancer une exception. Deuxièmement, vous devez obtenir une instance pour une classe spécifique. Si vous ne voulez pas de créer une instance comme ceci:

SingletonBase.getInstance(SecondSingletonTest.class) 

Vous pouvez créer des sous-classes comme ceci:

static class SingletonTest extends SingletonBase{ 
     public static SingletonTest getInstance(){ 
      return getInstance(SingletonTest.class); 
     } 
    } 

Il a également été suggéré d'utiliser l'approche ENUM, il est facile à mettre en œuvre, mais breakes ouvert fermé principe de SOLID

+0

La seule raison pour laquelle cela fonctionne est parce que toutes vos classes sont des classes imbriquées et peuvent accéder au constructeur privé. Je ne sais pas pourquoi vous ne l'avez pas affiché dans l'extrait de code. Cela brise le principe que vous avez mentionné beaucoup plus que l'énumération. Vous vous êtes fondamentalement mis en place la même chose que vous obtenez avec enum gratuitement. – Oleg

+0

@Oleg Eh bien, vous pouvez changer de constructeur pour protéger, et il fonctionnera toujours comme une base de singleton – mlecz