Quelques problèmes avec les singletons enum:
S'engager dans une stratégie de mise en œuvre
En général, "singleton" fait référence à une stratégie d'implémentation, et non à une spécification d'API. Il est très rare de Foo1.getInstance()
déclarer publiquement qu'il retournera toujours la même instance. Si nécessaire, l'implémentation de Foo1.getInstance()
peut évoluer, par exemple, pour renvoyer une instance par thread.
Avec Foo2.INSTANCE
nous déclarons publiquement que cette instance est l' instance, et il n'y a aucune chance de changer cela. La stratégie de mise en œuvre consistant à avoir une seule instance est exposée et engagée.
Ce problème n'est pas paralysant. Par exemple, Foo2.INSTANCE.doo()
peut s'appuyer sur un objet d'assistance local de thread, pour avoir effectivement une instance par thread.
Extension de la classe Enum
Foo2
étend une super classe Enum<Foo2>
. Nous voulons généralement éviter les super classes; surtout dans ce cas, la super classe forcée Foo2
n'a rien à voir avec ce qui Foo2
est censé être. C'est une pollution de la hiérarchie des types de notre application. Si nous voulons vraiment une super classe, c'est généralement une classe d'application, mais nous ne pouvons pas, Foo2
la super classe de est fixe.
Foo2
hérite de quelques méthodes d'instance amusantes comme name(), cardinal(), compareTo(Foo2)
, qui sont juste déroutantes pour Foo2
les utilisateurs de. Foo2
ne peut pas avoir sa propre name()
méthode même si cette méthode est souhaitable dans Foo2
l'interface de.
Foo2
contient également des méthodes statiques drôles
public static Foo2[] values() { ... }
public static Foo2 valueOf(String name) { ... }
public static <T extends Enum<T>> T valueOf(Class<T> enumType, String name)
qui semble être absurde pour les utilisateurs. Un singleton ne devrait généralement pas avoir de méthodes statiques pulbiques (autres que les getInstance()
)
Sérialisabilité
Il est très courant que les singletons soient avec état. Ces singletons ne doivent généralement pas être sérialisables. Je ne peux penser à aucun exemple réaliste où il est logique de transporter un singleton avec état d'une VM à une autre VM; un singleton signifie «unique au sein d'une machine virtuelle», et non «unique dans l'univers».
Si la sérialisation a vraiment un sens pour un singleton avec état, le singleton doit spécifier explicitement et précisément ce que signifie désérialiser un singleton dans une autre VM où un singleton du même type peut déjà exister.
Foo2
s'engage automatiquement dans une stratégie simpliste de sérialisation / désérialisation. Ce n'est qu'un accident qui attend de se produire. Si nous avons un arbre de données référençant conceptuellement une variable d'état de Foo2
dans VM1 à t1, par la sérialisation / désérialisation, la valeur devient une valeur différente - la valeur de la même variable de Foo2
dans VM2 à t2, créant un bug difficile à détecter. Ce bug n'arrivera pas à l'insérialisable en Foo1
silence.
Restrictions de codage
Il y a des choses qui peuvent être faites dans les classes normales, mais interdites dans les enum
classes. Par exemple, accéder à un champ statique dans le constructeur. Le programmeur doit être plus prudent car il travaille dans une classe spéciale.
Conclusion
En superposant sur enum, nous économisons 2 lignes de code; mais le prix est trop élevé, nous devons transporter tous les bagages et restrictions des énumérations, nous héritons par inadvertance des "caractéristiques" de l'énumération qui ont des conséquences inattendues. Le seul avantage allégué - la sérialisation automatique - se révèle être un inconvénient.