Nos últimos meses, tropecei algumas vezes na seguinte técnica / padrão. No entanto, não consigo encontrar um nome específico, nem tenho 100% de certeza sobre todas as suas vantagens e desvantagens.
O padrão é o seguinte:
Dentro de uma interface Java, um conjunto de métodos comuns é definido como de costume. No entanto, usando uma classe interna, uma instância padrão vaza pela interface.
public interface Vehicle {
public void accelerate();
public void decelerate();
public static class Default {
public static Vehicle getInstance() {
return new Car(); // or use Spring to retrieve an instance
}
}
}
Para mim, parece que a maior vantagem está no fato de que um desenvolvedor precisa apenas conhecer a interface e não suas implementações, por exemplo, caso ele queira criar rapidamente uma instância.
Vehicle someVehicle = Vehicle.Default.getInstance();
someVehicle.accelerate();
Além disso, vi essa técnica sendo usada junto com o Spring para fornecer instâncias dinamicamente, dependendo da configuração. Nesse sentido, também parece que isso pode ajudar na modularização.
No entanto, não posso deixar de pensar que isso é um uso indevido da interface, pois ela acopla a interface a uma de suas implementações. (Princípio de inversão de dependência etc.) Alguém poderia me explicar como é chamada essa técnica, bem como suas vantagens e desvantagens?
Atualizar:
Após algum tempo para consideração, verifiquei novamente e notei que a seguinte versão singleton do padrão era usada com muito mais frequência. Nesta versão, uma instância estática pública é exposta através da interface que é inicializada apenas uma vez (devido ao campo ser final). Além disso, a instância é quase sempre recuperada usando o Spring ou uma fábrica genérica que desacopla a interface da implementação.
public interface Vehicle {
public void accelerate();
public void decelerate();
public static class Default {
public static final Vehicle INSTANCE = getInstance();
private static Vehicle getInstance() {
return new Car(); // or use Spring/factory here
}
}
}
// Which allows to retrieve a singleton instance using...
Vehicle someVehicle = Vehicle.Default.INSTANCE;
Em poucas palavras: parece que esse é um padrão singleton / factory personalizado, que basicamente permite expor uma instância ou singleton por meio de sua interface. Com relação às desvantagens, algumas foram citadas nas respostas e comentários abaixo. Até agora, a vantagem parece estar na sua conveniência.
fonte
Vehicle.Default
deve ser movido para o namespace do pacote como uma classe de fábrica, por exemploVehicleFactory
.Vehicle.Default.getInstance() != Vehicle.Default.getInstance()
Respostas:
Default.getInstance
IMHO é apenas uma forma muito específica de um método de fábrica , misturada com uma convenção de nomenclatura tirada do padrão singleton (mas sem ser uma implementação deste último). Na forma atual, isso é uma violação do " princípio de responsabilidade única ", porque a interface (que já serve para declarar uma API de classe) assume a responsabilidade adicional de fornecer uma instância padrão, que seria muito melhor colocada em uma unidade separada.VehicleFactory
classe.O principal problema causado por essa construção é que ela induz uma dependência cíclica entre
Vehicle
eCar
. Por exemplo, no formulário atual, não será possível colocarVehicle
em uma biblioteca eCar
em outra. Usar uma classe de fábrica separada e colocar oDefault.getInstance
método lá resolveria esse problema. Também pode ser uma boa idéia dar um nome diferente a esse método, para evitar qualquer confusão relacionada aos singletons. Portanto, o resultado pode ser algo comoVehicleFactory.createDefaultInstance()
fonte
VehicleFactory
é mais convencional que a construção aninhadaVehicle.Default
, especialmente com o método "getInstance" enganoso. Embora seja possível contornar a dependência cíclica usando Spring, eu preferiria pessoalmente a primeira variante apenas por causa do bom senso. E, ainda assim, você tem a opção de mover a fábrica para um componente diferente, alterando a implementação posteriormente para funcionar sem o Spring etc.autodrive()
método. Seu carro muito genérico deve ser alterado para implementar esse método, por exemplo, lançando uma NotImplementedException, mas isso não confere um motivo adicional para a alteração no veículo.Parece -me o padrão Null Object .
Mas isso depende muito de como a
Car
classe é implementada. Se for implementado de maneira "neutra", é definitivamente um Objeto Nulo. Isso significa que, se você puder remover todas as verificações nulas na interface e colocar a instância dessa classe em vez de cada ocorrência denull
, o código ainda precisará funcionar corretamente.Além disso, se for esse padrão, não haverá problema em criar um acoplamento rígido entre a própria interface e a implementação do objeto nulo. Porque o objeto nulo pode estar em qualquer lugar onde a interface é usada.
fonte