Alguns problemas com enum singletons:
Comprometendo-se com uma estratégia de implementação
Normalmente, "singleton" refere-se a uma estratégia de implementação, não a uma especificação de API. É muito raro Foo1.getInstance()
declarar publicamente que sempre retornará a mesma instância. Se necessário, a implementação de Foo1.getInstance()
pode evoluir, por exemplo, para retornar uma instância por encadeamento.
Com Foo2.INSTANCE
declaramos publicamente que neste caso é o exemplo, e não há nenhuma chance de mudar isso. A estratégia de implementação de ter uma única instância é exposta e comprometida.
Este problema não é incapacitante. Por exemplo, Foo2.INSTANCE.doo()
pode contar com um objeto auxiliar local de encadeamento, para ter efetivamente uma instância por encadeamento.
Estendendo a classe Enum
Foo2
estende uma super classe Enum<Foo2>
. Geralmente, queremos evitar super classes; especialmente neste caso, a superclasse forçada Foo2
não tem nada a ver com o que Foo2
deveria ser. Isso é uma poluição para a hierarquia de tipos de nosso aplicativo. Se realmente queremos uma super classe, geralmente é uma classe de aplicação, mas não podemos, Foo2
a super classe é fixa.
Foo2
herda alguns métodos engraçados de instância como name(), cardinal(), compareTo(Foo2)
, que são apenas confusos para Foo2
os usuários. Foo2
não pode ter seu próprio name()
método, mesmo que esse método seja desejável na Foo2
interface.
Foo2
também contém alguns métodos estáticos engraçados
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 ser sem sentido para os usuários. Um singleton geralmente não deve ter métodos estáticos pulbicos de qualquer maneira (além do getInstance()
)
Serializability
É muito comum os singletons serem stateful. Esses singletons geralmente não devem ser serializáveis. Não consigo pensar em nenhum exemplo realista em que faça sentido transportar um singleton com estado de uma VM para outra VM; um singleton significa "único dentro de uma VM", não "único no universo".
Se a serialização realmente faz sentido para um singleton com estado, o singleton deve especificar explícita e precisamente o que significa desserializar um singleton em outra VM em que um singleton do mesmo tipo já exista.
Foo2
se compromete automaticamente com uma estratégia simplista de serialização / desserialização. Isso é apenas um acidente esperando para acontecer. Se tivermos uma árvore de dados referenciando conceitualmente uma variável de estado da Foo2
VM1 em t1, por serialização / desserialização, o valor se tornará um valor diferente - o valor da mesma variável da Foo2
VM2 em t2, criando um erro difícil de detectar. Este bug não acontecerá com o unserializable Foo1
silenciosamente.
Restrições de codificação
Há coisas que podem ser feitas nas aulas normais, mas proibidas nas enum
aulas. Por exemplo, acessando um campo estático no construtor. O programador precisa ter mais cuidado, pois está trabalhando em uma aula especial.
Conclusão
Ao pegar carona no enum, salvamos 2 linhas de código; mas o preço é muito alto, temos que carregar todas as bagagens e restrições de enums, inadvertidamente herdamos "características" de enum que têm conseqüências não intencionais. A única vantagem alegada - serialização automática - acaba sendo uma desvantagem.