Tenho algumas interfaces que pretendo que terceiros implementem no futuro e forneço uma implementação básica. Vou usar apenas alguns para mostrar o exemplo.
Atualmente, eles são definidos como
Item:
public interface Item {
String getId();
String getName();
}
ItemStack:
public interface ItemStackFactory {
ItemStack createItemStack(Item item, int quantity);
}
ItemStackContainer:
public interface ItemStackContainer {
default void add(ItemStack stack) {
add(stack, 1);
}
void add(ItemStack stack, int quantity);
}
Agora, Item
e ItemStackFactory
posso absolutamente prever que terceiros precisem estendê-lo no futuro. ItemStackContainer
também pode ser estendido no futuro, mas não das maneiras que posso prever, fora da implementação padrão fornecida.
Agora, estou tentando tornar essa biblioteca o mais robusta possível; isso ainda está nos estágios iniciais (pré-pré-alfa), portanto pode ser um ato de excesso de engenharia (YAGNI). Este é um local apropriado para usar genéricos?
public interface ItemStack<T extends Item> {
T getItem();
int getQuantity();
}
E
public interface ItemStackFactory<T extends ItemStack<I extends Item>> {
T createItemStack(I item, int quantity);
}
Receio que isso possa tornar as implementações e o uso mais difíceis de ler e entender; Eu acho que é a recomendação para evitar aninhar genéricos sempre que possível.
Respostas:
Você usa genéricos em sua interface quando é provável que sua implementação também seja genérica.
Por exemplo, qualquer estrutura de dados que possa aceitar objetos arbitrários é um bom candidato para uma interface genérica. Exemplos:
List<T>
eDictionary<K,V>
.Qualquer situação em que você queira melhorar a segurança de tipo de maneira generalizada é um bom candidato para genéricos. A
List<String>
é uma lista que opera apenas em cadeias.Qualquer situação em que você deseja aplicar o SRP de maneira generalizada e evitar métodos específicos de tipo é um bom candidato para genéricos. Por exemplo, um PersonDocument, PlaceDocument e ThingDocument podem ser substituídos por um
Document<T>
.O padrão Abstract Factory é um bom caso de uso para um genérico, enquanto um Método Factory comum simplesmente cria objetos que são herdados de uma interface comum e concreta.
fonte
class StackFactory { Stack<T> Create<T>(T item, int count); }
. Posso escrever um exemplo do que eu quis dizer com "refinar um parâmetro de tipo em uma subclasse" em uma resposta, se você desejar mais esclarecimentos, embora a resposta esteja relacionada tangencialmente à pergunta original.Seu plano de como introduzir generalidade em sua interface parece estar correto para mim. No entanto, sua pergunta sobre se isso seria uma boa ideia ou não requer uma resposta um pouco mais complicada.
Ao longo dos anos, entrevistei vários candidatos a cargos de Engenharia de Software em nome de empresas nas quais trabalhei e percebi que a maioria dos candidatos a emprego lá fora se sente confortável com o uso de classes genéricas (por exemplo, o classes de coleção padrão), mas não com a gravação de classes genéricas. A maioria nunca escreveu uma única classe genérica em sua carreira e parece que estaria completamente perdida se fosse solicitada a escrever uma. Portanto, se você forçar o uso de genéricos a alguém que queira implementar sua interface ou estender suas implementações básicas, poderá estar deixando de lado as pessoas.
O que você pode tentar, no entanto, é fornecer versões genéricas e não genéricas de suas interfaces e implementações básicas, para que as pessoas que não fazem genéricos possam viver felizes em seu mundo estreito e pesado, enquanto as pessoas que genéricos podem colher os benefícios de sua compreensão mais ampla do idioma.
fonte
A decisão é sua. Se você não fornecer genéricos, eles precisarão atualizar sua implementação
Item
sempre que usarem:Você deve pelo menos tornar
ItemStack
genérico da maneira que sugere. O benefício mais profundo dos genéricos é que você quase nunca precisa transmitir nada, e oferecer código que força os usuários a transmitir é IMHO um crime.fonte
Uau ... Eu tenho uma opinião completamente diferente sobre isso.
Isto é um erro. Você não deve escrever código "para o futuro".
Além disso, as interfaces são escritas de cima para baixo. Você não olha para uma classe e diz "Ei, essa classe poderia implementar totalmente uma interface e então eu posso usá-la em outro lugar também". Essa é uma abordagem errada, porque apenas a classe que usa uma interface sabe realmente o que a interface deve ser. Em vez disso, você deve estar pensando "Nesse ponto, minha classe precisa de uma dependência. Deixe-me definir uma interface para essa dependência. Quando terminar de escrever essa classe, escreverei uma implementação dessa dependência". Se alguém reutilizará sua interface e substituirá sua implementação padrão pela própria é totalmente irrelevante. Eles podem nunca fazer. Isso não significa que a interface foi inútil. Faz com que seu código siga o princípio Inversion of Control, que torna seu código extensível em muitos lugares "
fonte
Eu acho que o uso de genéricos na sua situação é muito complexo.
Se você escolher a versão genérica das interfaces, o design indicará que
Essas suposições e restrições implícitas são coisas muito importantes para se decidir com cuidado. Portanto, especialmente nos estágios iniciais, esses projetos prematuros não devem ser introduzidos. Simples, fácil de entender, design comum é desejado.
fonte
Eu diria que depende principalmente dessa pergunta: se alguém precisar estender a funcionalidade de
Item
eItemStackFactory
,No primeiro caso, não há necessidade de genéricos; no segundo, provavelmente existe.
fonte
Talvez você possa ter uma hierarquia de interfaces, onde Foo é uma interface e Bar é outra. Sempre que um tipo deve ser ambos, é um FooAndBar.
Para evitar excesso de desgin, faça apenas o tipo FooAndBar quando necessário e mantenha uma lista de interfaces que nunca estenderão nada.
Isso é muito mais confortável para a maioria dos programadores (a maioria gosta da verificação de tipo ou nunca gosta de digitar estática de qualquer tipo). Muito poucas pessoas escreveram sua própria aula de genéricos.
Também como nota: as interfaces são sobre quais ações possíveis podem ser executadas. Na verdade, eles devem ser verbos, não substantivos.
fonte