Como o princípio de segregação de interface sugere que nenhum cliente deve ser forçado a depender dos métodos que não usa, portanto, um cliente não deve implementar um método vazio para seus métodos de interface, caso contrário, esse método de interface deve ser colocado em outra interface.
Mas e os métodos concretos? Devo separar os métodos que nem todos os clientes usariam? Considere a seguinte classe:
public class Car{
....
public boolean isQualityPass(){
...
}
public int getTax(){
...
}
public int getCost(){
...
}
}
public class CarShop{
...
public int getCarPrice(int carId){
Car car=carList[carId];
int price=car.getTax() + car.getCost()... (some formula);
return price;
}
}
no código acima, o CarShop não usa o método isQualityPass () em Car, devo separar isQualityPass () em uma nova classe:
public class CheckCarQualityPass{
public boolean isQualityPass(Car car){
}
}
para reduzir o acoplamento do CarShop? Porque acho que uma vez se isQualityPass () precisa de dependência extra, por exemplo:
public boolean isQualityPass(){
HttpClient client=...
}
O CarShop dependeria do HttpClient, mesmo que ele nunca use o HttpClient. Portanto, minha pergunta é: de acordo com o princípio de segregação de interface, devo separar métodos concretos que nem todos os clientes usariam, para que esses métodos dependam do cliente somente quando o cliente realmente usar, para reduzir o acoplamento?
fonte
Car
classe que não deseja que todos (os) usuários criem, crie (mais de uma) interface que aCar
classe implemente e que declare apenas métodos úteis no contexto de interfaces.Respostas:
No seu exemplo,
CarShop
não dependeisQualityPass
e não é forçado a fazer uma implementação vazia para um método. Não há nem uma interface envolvida. Portanto, o termo "ISP" simplesmente não corresponde aqui. E enquanto um método como esseisQualityPass
for um método que se encaixa bem noCar
objeto, sem sobrecarregá-lo com responsabilidades ou dependências adicionais, tudo bem. Não há necessidade de refatorar um método público de uma classe para outro local apenas porque existe um cliente que não está usando o método.No entanto, tornar uma classe de domínio como
Car
diretamente dependente de algo comoHttpClient
provavelmente também não é uma boa ideia, independentemente de quais clientes usam ou não o método. Mover a lógica para uma classe separadaCheckCarQualityPass
simplesmente não é chamado de "ISP", isso é chamado de "separação de preocupações" . A preocupação de um objeto de carro reutilizável provavelmente não deve ser fazer chamadas HTTP externas, pelo menos não diretamente, isso limita a reutilização e, além disso, a testabilidade demais.Se
isQualityPass
não puder ser facilmente movido para outra classe, a alternativa seria fazer asHttp
chamadas através de uma interface abstrataIHttpClient
que é injetadaCar
no momento da construção ou injetando toda a estratégia de verificação "QualityPass" (com a solicitação Http encapsulada) noCar
objeto . Mas essa é a IMHO apenas a segunda melhor solução, pois aumenta a complexidade geral em vez de reduzi-la.fonte
Car
objeto mais complexo . Não seria minha primeira escolha para uma solução (pelo menos não no contexto deste exemplo artificial). No entanto, pode fazer mais sentido no código "real", não sei.O princípio de segregação de interface não se trata de proibir o acesso ao que você não precisa. É sobre não insistir no acesso ao que você não precisa.
As interfaces não são de propriedade da classe que as implementa. Eles pertencem aos objetos que os usam.
O que é usado aqui é
getTax()
egetCost()
. O que está sendo insistido é tudo acessívelCar
. O problema é insistirCar
significa que está insistindo no acesso aoisQualityPass()
qual não é necessário.Isso pode ser consertado. Você pergunta se pode ser corrigido concretamente. Pode.
Nenhum desses códigos sequer sabe se
CarLiability
é uma interface ou uma classe concreta. Isso é uma coisa boa. Não quer saber.Se é uma interface
Car
pode implementá-lo. Isso não violaria o ISP, porque, apesar deisQuality()
estar presenteCar
CarShop
, não insiste nisso. Isto é bom.Se for concreto, pode ser que
isQuality()
ou não exista ou tenha sido transferido para outro lugar. Isto é bom.Também pode ser que
CarLiability
seja um invólucro de concretoCar
que esteja delegando trabalho a ele. ContantoCarLiability
que não exponhaisQuality()
, tudoCarShop
bem. É claro que isso apenas chuta a lata no caminho eCarLiability
tem que descobrir como seguir o ISPCar
da mesma maneira queCarShop
tinha que fazer.Em resumo,
isQuality()
não precisa ser removido porCar
causa do ISP. A necessidade implícita precisaisQuality()
ser removidaCarShop
porqueCarShop
não precisa, portanto, não deve solicitá-la.fonte
Na verdade não. Existem diferentes maneiras para se esconder
Car.isQualityPass
deCarShop
.1. Modificadores de acesso
Do ponto de vista da Lei de Deméter , poderíamos considerar
Car
eCardShop
não ser amigos . Ele nos legitima a fazer o próximo.Esteja ciente de que ambos os componentes estão em pacotes diferentes. Agora
CarShop
não tem visibilidade sobre comportamentosCar
protegidos . (Com licença, se o exemplo acima parecer tão simplista).2. Segregação de Interface
O ISP trabalha com a premissa de que trabalhamos com abstrações, não com classes concretas. Assumirei que você já esteja familiarizado com a implementação do ISP e com as interfaces de função .
Apesar da
Car
implementação real , nada nos impede de praticar o ISP.O que eu fiz aqui. Eu reduzi a interação entre
Car
eCarShop
através da interface de função Billable . Esteja ciente da alteração nagetPrice
assinatura. Modifiquei intencionalmente o argumento. Eu queria deixar óbvio queCarShop
é apenas "vinculado / vinculado" a uma das interfaces de função disponíveis. Eu poderia ter seguido a implementação real, mas não conheço os detalhes reais da implementação e tenho medo dogetPrice(String carId)
acesso real (visibilidade) sobre a classe concreta. Se tiver, todo o trabalho feito com o ISP se torna inútil, porque está nas mãos do desenvolvedor fazer casting e trabalhar apenas com a interface Billable . Não importa quão metódicos somos, a tentação sempre estará lá.3. Responsabilidade única
Receio não estar em posição de dizer se a dependência entre
Car
eHttpClient
é adequada, mas concordo com o @DocBrown, isso gera alguns avisos que valem a pena uma revisão de design. Nem a Lei de Demeter nem o ISP tornarão seu design "melhor" neste momento. Eles apenas mascaram o problema, não o corrigem.Sugeri ao DocBrown o Padrão de Estratégia como uma possível solução. Concordei com ele que o padrão acrescenta complexidade, mas também acho que qualquer re-design o fará. É uma troca, quanto mais dissociação queremos, mais partes móveis temos (geralmente). De qualquer forma, acho que ambos concordam com um novo design, é altamente recomendável.
Resumindo
Não, você não precisa mover métodos concretos para classes externas para não torná-las acessíveis. Pode haver inúmeros consumidores. Você mudaria todos os métodos concretos para classes externas toda vez que um novo consumidor entra em jogo? Espero que não.
fonte