Muitas vezes me encontro lutando para decidir qual dessas duas maneiras usar quando preciso usar dados comuns em alguns métodos de minhas classes. Qual seria uma escolha melhor?
Nesta opção, eu posso criar uma variável de instância para evitar a necessidade de declarar variáveis adicionais e também para definir parâmetros de método, mas pode não estar tão claro onde essas variáveis estão sendo instanciadas / modificadas:
public class MyClass {
private int var1;
MyClass(){
doSomething();
doSomethingElse();
doMoreStuff();
}
private void doSomething(){
var1 = 2;
}
private void doSomethingElse(){
int var2 = var1 + 1;
}
private void doMoreStuff(){
int var3 = var1 - 1;
}
}
Ou apenas instanciar variáveis locais e passá-las como argumentos?
public class MyClass {
MyClass(){
int var1 = doSomething();
doSomethingElse(var1);
doMoreStuff(var1);
}
private int doSomething(){
int var = 2;
return var;
}
private void doSomethingElse(int var){
int var2 = var + 1;
}
private void doMoreStuff(int var){
int var3 = var - 1;
}
}
Se a resposta é que ambos estão corretos, qual deles é visto / usado com mais frequência? Além disso, se você puder fornecer prós / contras adicionais para cada opção, seria muito valioso.
java
coding-style
Carlossierra
fonte
fonte
Respostas:
Estou surpreso que isso ainda não tenha mencionado ...
Depende se
var1
é realmente parte do estado do seu objeto .Você assume que essas duas abordagens estão corretas e que é apenas uma questão de estilo. Você está errado.
Isso é inteiramente sobre como modelar corretamente.
Da mesma forma,
private
existem métodos de instância para alterar o estado do seu objeto . Se não é isso que seu método está fazendo, deve serprivate static
.fonte
transient
não tem nada a ver com isso, porquetransient
se trata de estado persistente , como em partes do objeto que são salvas quando você faz algo como serializar o objeto. Por exemplo, o repositório de backup de um ArrayList étransient
totalmente crucial para o estado do ArrayList, porque quando você serializa um ArrayList, deseja salvar apenas a parte do repositório que contém elementos reais do ArrayList, não o espaço livre no final, reservado para mais adições de elementos.var1
for necessário para alguns métodos, mas não parte do estado deMyClass
, talvez seja hora de colocarvar1
esses métodos em outra classe a ser usadaMyClass
.Não sei qual é mais prevalente, mas sempre faria o último. Ele comunica mais claramente o fluxo de dados e a vida útil e não incha todas as instâncias da sua classe com um campo cuja única vida útil relevante é durante a inicialização. Eu diria que o primeiro é apenas confuso e torna a revisão de código significativamente mais difícil, porque tenho que considerar a possibilidade de qualquer método ser modificado
var1
.fonte
Você deve reduzir o escopo de suas variáveis o máximo possível (e razoável). Não apenas em métodos, mas em geral.
Para sua pergunta, isso significa que depende se a variável faz parte do estado do objeto. Se sim, não há problema em usá-lo nesse escopo, ou seja, todo o objeto. Neste caso, vá com a primeira opção. Se não, escolha a segunda opção, pois ela reduz a visibilidade das variáveis e, portanto, a complexidade geral.
fonte
Há outro estilo - use um contexto / estado.
Há vários benefícios nessa abordagem. Os objetos de estado podem mudar independentemente do objeto, por exemplo, dando muito espaço de manobra para o futuro.
Esse é um padrão que também funciona bem em um sistema distribuído / servidor em que alguns detalhes devem ser preservados nas chamadas. Você pode armazenar detalhes do usuário, conexões com o banco de dados, etc. no
state
objeto.fonte
É sobre efeitos colaterais.
Perguntar se
var1
é parte do Estado erra o objetivo desta questão. Claro que sevar1
deve persistir, tem que ser uma instância. Qualquer uma das abordagens pode ser feita para trabalhar se a persistência é necessária ou não.A abordagem do efeito colateral
Algumas variáveis de instância são usadas apenas para se comunicar entre métodos privados de chamada em chamada. Esse tipo de variável de instância pode ser refatorado fora da existência, mas não precisa ser. Às vezes as coisas são mais claras com eles. Mas isso não é isento de riscos.
Você está deixando uma variável fora de seu escopo porque é usada em dois escopos particulares diferentes. Não porque é necessário no escopo que você está colocando. Isso pode ser confuso. Os "globais são maus!" nível de confusão. Isso pode funcionar, mas não será bem dimensionado. Só funciona no pequeno. Não há grandes objetos. Não há longas cadeias de herança. Não cause um efeito ioiô .
A abordagem funcional
Agora, mesmo que
var1
deva persistir, nada indica que você deve usá-lo para cada valor transitório que possa assumir antes de atingir o estado que você deseja preservar entre chamadas públicas. Isso significa que você ainda pode definir umavar1
instância usando nada além de métodos mais funcionais.Portanto, parte do estado ou não, você ainda pode usar qualquer uma das abordagens.
Nestes exemplos, 'var1' é tão encapsulado que nada além do seu depurador sabe que ele existe. Suponho que você fez isso deliberadamente porque não quer nos influenciar. Felizmente eu não ligo para qual.
O risco de efeitos colaterais
Dito isto, eu sei de onde vem sua pergunta. Eu trabalhei sob a miserável herança de yo yo 'que modifica uma variável de instância em vários níveis em vários métodos e me esqueci tentando segui-la. Esse é o risco.
Essa é a dor que me leva a uma abordagem mais funcional. Um método pode documentar suas dependências e saída em sua assinatura. Essa é uma abordagem clara e poderosa. Também permite alterar o que você passa no método privado, tornando-o mais reutilizável dentro da classe.
A vantagem dos efeitos colaterais
Também é limitador. Funções puras não têm efeitos colaterais. Isso pode ser uma coisa boa, mas não é orientada a objetos. Uma grande parte da orientação a objetos é a capacidade de se referir a um contexto fora do método. Fazer isso sem vazar globais por aqui e por aí vai é a força da OOP. Eu recebo a flexibilidade de um global, mas ele está bem contido na classe. Eu posso chamar um método e alterar todas as variáveis de instância de uma só vez, se eu quiser. Se fizer isso, sou obrigado a pelo menos dar um nome ao método que deixe claro o que está acontecendo, para que as pessoas não se surpreendam quando isso acontecer. Comentários também podem ajudar. Às vezes, esses comentários são formalizados como "pós-condições".
A desvantagem dos métodos privados funcionais
A abordagem funcional esclarece algumas dependências. A menos que você esteja em uma linguagem funcional pura, ele não pode excluir dependências ocultas. Você não sabe, olhando apenas para uma assinatura de métodos, que não está escondendo um efeito colateral de você no restante do código. Você simplesmente não.
Postar condicionais
Se você e todos os outros membros da equipe documentam com segurança os efeitos colaterais (condições pré / pós) nos comentários, o ganho da abordagem funcional é muito menor. Sim, eu sei, sonhe.
Conclusão
Pessoalmente, eu tendem a usar métodos privados funcionais em ambos os casos, se puder, mas honestamente é principalmente porque esses comentários de efeitos colaterais condicionais pré / pós não causam erros de compilador quando estão desatualizados ou quando os métodos são chamados fora de ordem. A menos que eu realmente precise da flexibilidade dos efeitos colaterais, prefiro apenas saber que as coisas funcionam.
fonte
var1
como uma variável de estado alterando paradigmas. O escopo da classe NÃO é exatamente onde o estado está armazenado. É também um escopo anexo. Isso significa que existem duas motivações possíveis para colocar uma variável no nível da classe. Você pode reivindicar fazê-lo apenas para o escopo anexo e não para o estado ser mau, mas eu digo que é uma troca com a aridade que também é má. Eu sei disso porque tive que manter o código que faz isso. Alguns foram bem feitos. Alguns foi um pesadelo. A linha entre os dois não é estado. É legibilidade.A primeira variante parece não intuitiva e potencialmente perigosa para mim (imagine por qualquer motivo que alguém torne seu método privado público).
Eu preferiria instanciar suas variáveis na construção da classe ou passá-las como argumentos. O último oferece a opção de usar idiomas funcionais e não depender do estado do objeto que o contém.
fonte
Já existem respostas falando sobre o estado do objeto e quando o segundo método é preferido. Eu só quero adicionar um caso de uso comum para o primeiro padrão.
O primeiro padrão é perfeitamente válido quando tudo o que sua classe faz é encapsular um algoritmo . Um caso de uso para isso é que, se você escrevesse o algoritmo em um único método, ele seria muito grande. Então, você a divide em métodos menores, torna uma classe e torna os sub métodos privados.
Agora, passar todo o estado do algoritmo por meio de parâmetros pode ser entediante, então você usa os campos privados. Também está de acordo com a regra do primeiro parágrafo, porque é basicamente um estado da instância. Você só deve ter em mente e documentar adequadamente que o algoritmo não é reentrante se você usar campos particulares para isso . Isso não deve ser um problema na maioria das vezes, mas pode morder você.
fonte
Vamos tentar um exemplo que faça alguma coisa. Perdoe-me, pois este é javascript, não java. O ponto deve ser o mesmo.
Visite https://blockly-games.appspot.com/pond-duck?lang=en , clique na guia javascript e cole:
Você deve perceber que
dir
,wid
edis
não estão sendo passados ao redor muito. Você também deve observar que o código na função quelock()
retorna se parece muito com pseudo-código. Bem, é um código real. No entanto, é muito fácil de ler. Poderíamos adicionar aprovação e atribuição, mas isso acrescentaria uma confusão que você nunca veria em pseudo-código.Se você deseja argumentar que não executar a atribuição e aprovação é bom porque esses três vars são de estado persistente, considere uma reformulação que atribua
dir
um valor aleatório a cada loop. Não é persistente agora, é?Claro, agora podemos diminuir
dir
o escopo agora, mas não sem sermos forçados a desordenar nosso código pseudo-semelhante com a passagem e a configuração.Portanto, não, o estado não é o motivo pelo qual você decide usar ou não os efeitos colaterais, em vez de passar e retornar. Os efeitos colaterais também não significam que seu código é ilegível. Você não obtém os benefícios do código funcional puro . Mas bem feitos, com bons nomes, eles podem realmente ser legais de ler.
Isso não quer dizer que eles não possam se transformar em um espaguete como um pesadelo. Mas então, o que não pode?
Feliz caça ao pato.
fonte