Algunos problemas con enum singletons:
Comprometerse con una estrategia de implementación
Normalmente, "singleton" se refiere a una estrategia de implementación, no a una especificación de API. Es muy raro Foo1.getInstance()
declarar públicamente que siempre devolverá la misma instancia. Si es necesario, la implementación de Foo1.getInstance()
puede evolucionar, por ejemplo, para devolver una instancia por hilo.
Con Foo2.INSTANCE
declaramos públicamente que esta instancia es la instancia, y no hay posibilidad de cambiar eso. La estrategia de implementación de tener una sola instancia está expuesta y comprometida.
Este problema no es paralizante. Por ejemplo, Foo2.INSTANCE.doo()
puede confiar en un objeto auxiliar local de subprocesos para tener una instancia por subproceso de manera efectiva .
Ampliando la clase Enum
Foo2
extiende una super clase Enum<Foo2>
. Por lo general, queremos evitar las superclases; especialmente en este caso, la superclase forzada Foo2
no tiene nada que ver con lo que Foo2
se supone que es. Eso es una contaminación para la jerarquía de tipos de nuestra aplicación. Si realmente queremos una superclase, generalmente es una clase de aplicación, pero no podemos, Foo2
la superclase es fija.
Foo2
hereda algunos métodos divertidos de instancia como name(), cardinal(), compareTo(Foo2)
, que son confusos para Foo2
los usuarios de. Foo2
no puede tener su propio name()
método incluso si ese método es deseable en Foo2
la interfaz de.
Foo2
también contiene algunos métodos estáticos divertidos
public static Foo2[] values() { ... }
public static Foo2 valueOf(String name) { ... }
public static <T extends Enum<T>> T valueOf(Class<T> enumType, String name)
que parece no tener sentido para los usuarios. Un singleton generalmente no debería tener métodos estáticos pulbicos de todos modos (aparte del getInstance()
)
Serializabilidad
Es muy común que los singletons tengan estado. Estos singletons generalmente no deben ser serializables. No se me ocurre ningún ejemplo realista en el que tenga sentido transportar un singleton con estado de una VM a otra VM; un singleton significa "único dentro de una VM", no "único en el universo".
Si la serialización realmente tiene sentido para un singleton con estado, el singleton debería especificar de manera explícita y precisa qué significa deserializar un singleton en otra VM donde ya puede existir un singleton del mismo tipo.
Foo2
se compromete automáticamente con una estrategia de serialización / deserialización simplista. Eso es solo un accidente esperando a suceder. Si tenemos un árbol de datos que hace referencia conceptual a una variable de estado de Foo2
VM1 en t1, a través de la serialización / deserialización, el valor se convierte en un valor diferente: el valor de la misma variable de Foo2
VM2 en t2, creando un error difícil de detectar. Este error no le ocurrirá a los no serializables en Foo1
silencio.
Restricciones de codificación
Hay cosas que se pueden hacer en las clases normales, pero prohibidas en las enum
clases. Por ejemplo, acceder a un campo estático en el constructor. El programador tiene que tener más cuidado ya que está trabajando en una clase especial.
Conclusión
Al llevar a cuestas enum, ahorramos 2 líneas de código; pero el precio es demasiado alto, tenemos que llevar todos los equipajes y restricciones de las enumeraciones, heredamos inadvertidamente "características" de la enumeración que tienen consecuencias no deseadas. La única supuesta ventaja (serialización automática) resulta ser una desventaja.