Eu cresci usando um princípio para projetar e consumir interfaces que diz basicamente: "peça apenas o que você precisa".
Por exemplo, se eu tiver vários tipos que podem ser excluídos, farei uma Deletable
interface:
interface Deletable {
void delete();
}
Então eu posso escrever uma classe genérica:
class Deleter<T extends Deletable> {
void delete(T t) {
t.delete();
}
}
Em outro lugar no código, sempre solicitarei a menor responsabilidade possível para atender às necessidades do código do cliente. Portanto, se eu precisar excluir apenas um File
, ainda pedirei um Deletable
, não um File
.
Esse princípio é de conhecimento comum e já tem um nome aceito? É controverso? Isso é discutido em livros didáticos?
object-oriented
interfaces
solid
single-responsibility
glenviewjeff
fonte
fonte
Respostas:
Eu acredito que isso se refere ao que Robert Martin chama de Princípio de Segregação de Interface . As interfaces são separadas em pequenas e concisas para que os consumidores (clientes) precisem apenas conhecer os métodos que lhes interessam. Você pode conferir mais sobre o SOLID .
fonte
Para expandir a resposta muito boa de Vadim, responderei à pergunta "é controverso" com "não, não realmente".
Em geral, a segregação de interface é uma coisa boa, reduzindo o número geral de "razões para mudar" dos vários objetos envolvidos. O princípio principal é que, quando uma interface com vários métodos deve ser alterada, por exemplo, para adicionar um parâmetro a um dos métodos de interface, todos os consumidores da interface devem pelo menos ser recompilados, mesmo que não usem o método que mudou. "Mas é apenas uma recompilação!", Eu ouvi você dizer; isso pode ser verdade, mas lembre-se de que normalmente qualquer coisa que você recompilar deve ser enviada como parte de um patch de software, independentemente da importância da mudança no binário. Essas regras foram originalmente conceituadas no início dos anos 90, quando a estação de trabalho de desktop média era menos poderosa do que o telefone no bolso, a discagem de 14,4 baud era incrível e 3,5 "disquetes de 1,44 MB" eram a principal mídia removível. Mesmo na era atual do 3G / 4G, os usuários de internet sem fio costumam ter planos de dados com limites; portanto, ao liberar uma atualização, menos binários precisam ser baixados, melhor.
No entanto, como todas as boas idéias, a segregação de interface pode ficar ruim se implementada incorretamente. Primeiramente, existe a possibilidade de que, segregando interfaces e mantendo o objeto que implementa essas interfaces (cumprindo as dependências) relativamente inalterado, você possa acabar com uma "Hydra", parente do antipadrão "Objeto Deus", onde o objeto a natureza onisciente e onipotente do objeto está oculta do dependente pelas interfaces estreitas. Você acaba com um monstro de muitas cabeças que é pelo menos tão difícil de manter quanto o Objeto de Deus, além da sobrecarga de manter todas as suas interfaces. Não há um número difícil de interfaces que você não deve exceder, mas cada interface que você implementa em um único objeto deve ser precedida pela resposta à pergunta: "Essa interface contribui para o objeto '
Segundo, uma interface por método pode não ser necessária, apesar do que o SRP possa lhe dizer. Você pode acabar com "código ravioli"; tantos pedaços pequenos que é difícil rastrear para descobrir exatamente onde as coisas realmente acontecem. Também não é necessário dividir uma interface com dois métodos se todos os usuários atuais dessa interface precisarem dos dois métodos. Mesmo que uma das classes dependentes precise apenas de um dos dois métodos, geralmente é aceitável não dividir a interface se seus métodos tiverem coesão muito alta (bons exemplos são "métodos antônimos", que são exatamente opostos um do outro).
A segregação da interface deve ser baseada nas classes que dependem da interface:
Se houver apenas uma classe dependente da interface, não separe. Se a classe não usa um ou mais dos métodos de interface e é o único consumidor da interface, é provável que você não deva ter exposto esses métodos em primeiro lugar.
Se houver mais de uma classe que depende da interface e todos os dependentes usarem todos os métodos da interface, não separe; se você precisar alterar a interface (para adicionar um método ou alterar uma assinatura), todos os consumidores atuais serão afetados pela alteração, independentemente de você segregar ou não (embora, se você estiver adicionando um método que pelo menos um dependente não precisará, considere cuidadosamente se a alteração for implementada como uma nova interface, possivelmente herdada da existente).
Se houver mais de uma classe dependente da interface e eles não usarem os mesmos métodos, é um candidato à segregação. Veja a "coerência" da interface; todos os métodos promovem um objetivo de programação único e muito específico? Se você conseguir identificar mais de um objetivo principal para a interface (e seus implementadores), considere dividir as interfaces nessas linhas para criar interfaces menores com menos "motivos para mudar".
fonte
IBaz : IFoo, IBar
e exigir isso.IFoo
eIBar
, definir uma composiçãoIFooBar
pode ser uma boa ideia, mas se as interfaces forem bem divididas, é fácil acabar exigindo dezenas de tipos de interface distintos. Considere os seguintes recursos que as coleções podem ter: Enumerar, reportar Contagem, Ler enésimo elemento, Escrever enésimo elemento, Inserir antes de enésimo elemento, Excluir enésimo elemento, Novo item (ampliar coleção e retornar índice de novo espaço) e Incluir. Nove métodos: ECRWIDNA. Eu provavelmente poderia descrever dezenas de tipos que naturalmente suportariam muitas combinações diferentes.