Basicamente, preciso executar ações diferentes, dada uma determinada condição. O código existente é escrito desta maneira
Interface base
// DoSomething.java
interface DoSomething {
void letDoIt(String info);
}
Implementação da primeira classe de trabalhadores
class DoItThisWay implements DoSomething {
...
}
Implementação da segunda classe de trabalhadores
class DoItThatWay implements DoSomething {
...
}
A classe principal
class Main {
public doingIt(String info) {
DoSomething worker;
if (info == 'this') {
worker = new DoItThisWay();
} else {
worker = new DoItThatWay();
}
worker.letDoIt(info)
}
Este código funciona bem e é fácil de entender.
Agora, devido a um novo requisito, preciso passar uma nova informação que só faz sentido DoItThisWay
.
Minha pergunta é: o seguinte estilo de codificação é bom para lidar com esse requisito.
Use nova variável de classe e método
// Use new class variable and method
class DoItThisWay implements DoSomething {
private int quality;
DoSomething() {
quality = 0;
}
public void setQuality(int quality) {
this.quality = quality;
};
public void letDoIt(String info) {
if (quality > 50) { // make use of the new information
...
} else {
...
}
} ;
}
Se eu fizer dessa maneira, preciso fazer a alteração correspondente no chamador:
class Main {
public doingIt(String info) {
DoSomething worker;
if (info == 'this') {
int quality = obtainQualityInfo();
DoItThisWay tmp = new DoItThisWay();
tmp.setQuality(quality)
worker = tmp;
} else {
worker = new DoItThatWay();
}
worker.letDoIt(info)
}
É um bom estilo de codificação? Ou posso apenas lançá-lo
class Main {
public doingIt(String info) {
DoSomething worker;
if (info == 'this') {
int quality = obtainQualityInfo();
worker = new DoItThisWay();
((DoItThisWay) worker).setQuality(quality)
} else {
worker = new DoItThatWay();
}
worker.letDoIt(info)
}
java
coding-style
Anthony Kong
fonte
fonte
quality
para o construtorDoItThisWay
?DoItThisWay
eDoItThatWay
é feita uma vez no construtor deMain
.Main
é uma classe de vida longa edoingIt
é chamada várias vezes.setQuality
método será chamado várias vezes durante a vida útil doDoItThisWay
objeto?Respostas:
Estou assumindo que
quality
precisa ser definido ao lado de cadaletDoIt()
chamada em umDoItThisWay()
.A questão que vejo surgir aqui é a seguinte: Você está introduzindo um acoplamento temporal (ou seja, o que acontece se você esquecer de ligar
setQuality()
antes de chamarletDoIt()
umDoItThisWay
?). E as implementações paraDoItThisWay
eDoItThatWay
são divergentes (é preciso tersetQuality()
chamado, o outro não).Embora isso possa não causar problemas no momento, ele pode voltar a assombrá-lo eventualmente. Talvez valha a pena dar uma olhada
letDoIt()
e considerar se as informações de qualidade precisam fazer parte do queinfo
você passa por elas; mas isso depende dos detalhes.fonte
Do meu ponto de vista, nenhuma das suas versões é. Primeiro, ter que ligar
setQuality
antesletDoIt
pode ser chamado é um acoplamento temporal . Você está preso vendoDoItThisWay
como um derivado deDoSomething
, mas não é (pelo menos não funcionalmente), é algo como umIsso tornaria
Main
algo comoPor outro lado, você poderia passar os parâmetros diretamente para as classes (supondo que isso seja possível) e delegar a decisão sobre qual delas usar em uma fábrica (isso tornaria as instâncias intercambiáveis novamente e ambas poderão ser derivadas
DoSomething
). Isso ficariaMain
assimEu sei que você escreveu isso
Mas você pode armazenar em cache as peças que são caras para criar na fábrica e passá-las ao construtor também.
fonte
DoItThisWay
requer que a qualidade seja definida antes de chamá-letDoIt
la se comporta de maneira diferente. Eu esperariaDoSomething
que funcionasse da mesma maneira (sem definir a qualidade antes) para todos os derivados. Isso faz sentido? É claro que isso é meio embaçado, já que se chamássemos osetQuality
método de uma fábrica, o cliente ficaria agnóstico sobre se a qualidade deve ser definida ou não.letDoIt()
é (sem informações adicionais) "Posso executar corretamente (fazer o que fizer) se você me fornecerString info
". Exigir quesetQuality()
seja chamado antecipadamente reforça a pré-condição de chamadaletDoIt()
e, portanto, seria uma violação do LSP.letDoIt
" não lançasse nenhuma exceção, e esquecendo de chamar "setQuality" criarialetDoIt
uma exceção, então eu concordaria que tal implementação violaria o LSP. Mas essas parecem suposições muito artificiais para mim.Vamos supor
quality
que não possa ser passado para o construtor, e a chamada parasetQuality
é necessária.Atualmente, um trecho de código como
é muito pequeno para investir pensamentos demais nele. IMHO parece um pouco feio, mas não é realmente difícil de entender.
Os problemas surgem quando esse snippet de código cresce e, devido à refatoração, você acaba com algo como
Agora, você não vê imediatamente se esse código ainda está correto e o compilador não informará. Portanto, introduzir uma variável temporária do tipo correto e evitar o elenco é um pouco mais seguro para o tipo, sem nenhum esforço extra real.
No entanto, eu realmente daria
tmp
um nome melhor:Essa nomeação ajuda a fazer com que o código errado pareça errado.
fonte
DoItThisWay
obtém parâmetros mais específicos - pelo que você sugere, o nome precisa ser alterado sempre. É melhor usar nomes para variáveis que tornam o tipo de objeto claro, não quais nomes de métodos estão disponíveis.workerThisWay
se torne algo parecidousingQuality
. Nesse pequeno trecho de código, é mais seguro ser mais específico. (E, se houver uma frase melhor em seu domínio, do que "usingQuality", use-a. #Uma interface comum em que a inicialização varia com base nos dados de tempo de execução geralmente se presta ao padrão de fábrica.
Então DoThingsFactory pode se preocupar em obter e definir as informações de qualidade dentro do
getDoer(info)
método antes de retornar o objeto DoThingsWithQuality concreto convertido para a interface DoSomething.fonte