De acordo com a resposta aceita em " Racional para preferir variáveis locais em vez de variáveis de instância? ", As variáveis devem viver no menor escopo possível.
Simplifique o problema na minha interpretação, significa que devemos refatorar esse tipo de código:
public class Main {
private A a;
private B b;
public ABResult getResult() {
getA();
getB();
return ABFactory.mix(a, b);
}
private getA() {
a = SomeFactory.getA();
}
private getB() {
b = SomeFactory.getB();
}
}
em algo como isto:
public class Main {
public ABResult getResult() {
A a = getA();
B b = getB();
return ABFactory.mix(a, b);
}
private getA() {
return SomeFactory.getA();
}
private getB() {
return SomeFactory.getB();
}
}
mas de acordo com o "espírito" de "variáveis deve viver no menor escopo possível", "nunca tem variáveis" tem escopo menor do que "tem variáveis"? Então, acho que a versão acima deve ser reformulada:
public class Main {
public ABResult getResult() {
return ABFactory.mix(getA(), getB());
}
private getA() {
return SomeFactory.getA();
}
private getB() {
return SomeFactory.getB();
}
}
para que getResult()
não tenha nenhuma variável local. Isso é verdade?
refactoring
scope
local-variable
ocomfd
fonte
fonte
final
palavras-chave ou não.Respostas:
Não. Existem várias razões pelas quais:
E assim por diante.
fonte
var taxIndex = getTaxIndex();
).now()
,) remover a variável e chamar o método mais de uma vez pode resultar em erros. Isso pode criar uma situação realmente sutil e difícil de depurar. Pode parecer óbvio, mas se você estiver em uma missão cega de refatoração para remover variáveis, é fácil acabar apresentando falhas.Concordo, variáveis que não são necessárias e que não melhoram a legibilidade do código devem ser evitadas. Quanto mais variáveis estiverem no escopo em um determinado ponto do código, mais complexo esse código será o entendimento.
Eu realmente não vejo o benefício das variáveis
a
eb
no seu exemplo, então eu escreveria a versão sem variáveis. Por outro lado, a função é tão simples em primeiro lugar que não acho que isso importe muito.Torna-se mais um problema, quanto mais a função fica e mais variáveis estão no escopo.
Por exemplo, se você tiver
Na parte superior de uma função maior, você aumenta a carga mental de entender o restante do código, introduzindo três variáveis em vez de uma. Você tem que ler o resto do código para ver se
a
oub
são utilizados novamente. Os locais que estão no escopo por mais tempo do que precisam são ruins para a legibilidade geral.Claro que no caso em que uma variável é necessário (por exemplo, para armazenar um resultado temporário), ou quando uma variável faz melhorar a legibilidade do código, em seguida, ele deve ser mantido.
fonte
var result = getResult(...); return result;
é o caso de você poder colocar um ponto de interrupçãoreturn
e aprender exatamente o queresult
é.Além das outras respostas, gostaria de apontar outra coisa. O benefício de manter o escopo de uma variável pequeno não é apenas reduzir a quantidade de código sintaticamente disponível para a variável, mas também o número de caminhos possíveis do fluxo de controle que podem potencialmente modificar uma variável (atribuindo-lhe um novo valor ou chamando um método de mutação no objeto existente mantido na variável).
Variáveis com escopo de classe (instância ou estática) têm significativamente mais caminhos de fluxo de controle possíveis do que variáveis com escopo local porque podem ser mutadas por métodos, que podem ser chamados em qualquer ordem, qualquer número de vezes e geralmente por código fora da classe .
Vamos dar uma olhada no seu
getResult
método inicial :Agora, os nomes
getA
egetB
pode sugerir que eles vão atribuir athis.a
ethis.b
, não podemos saber com certeza a partir de apenas olhandogetResult
. Assim, é possível que os valoresthis.a
ethis.b
passados para omix
método venham do estado dothis
objeto de antes quegetResult
foi invocado, o que é impossível prever, pois os clientes controlam como e quando os métodos são chamados.No código revisado com o local
a
e asb
variáveis, fica claro que existe exatamente um fluxo de controle (sem exceção) da atribuição de cada variável ao seu uso, porque as variáveis são declaradas logo antes de serem usadas.Portanto, há um benefício significativo em mover variáveis (modificáveis) do escopo de classe para o escopo local (bem como mover variáveis (modificáveis) do exterior de um loop para o interior), pois simplifica o raciocínio do fluxo de controle.
Por outro lado, eliminar variáveis como no seu último exemplo tem menos benefícios, porque realmente não afeta o raciocínio do fluxo de controle. Você também perde os nomes dados aos valores, o que não acontece ao simplesmente mover uma variável para um escopo interno. Este é um trade-off que você deve considerar; portanto, a eliminação de variáveis pode ser melhor em alguns casos e pior em outros.
Se você não quiser perder os nomes das variáveis, mas ainda assim reduzir o escopo das variáveis (no caso delas serem usadas em uma função maior), considere agrupar as variáveis e seus usos em uma instrução de bloco ( ou movê-los para sua própria função ).
fonte
Isso depende um pouco da linguagem, mas eu diria que um dos benefícios menos óbvios da programação funcional é o fato de incentivar o programador e o leitor de código a não precisar deles. Considerar:
Ou algum LINQ:
Ou Node.js:
A última é uma cadeia de chamar uma função no resultado de uma função anterior, sem nenhuma variável intermediária. Apresentá-los tornaria muito menos claro.
No entanto, a diferença entre o primeiro exemplo e os outros dois é a ordem implícita de operação . Pode não ser o mesmo que a ordem em que realmente é calculada, mas é a ordem na qual o leitor deve pensar sobre isso. Nos dois segundos, isso é deixado para a direita. Para o exemplo Lisp / Clojure, é mais da direita para a esquerda. Você deve ser um pouco cauteloso ao escrever código que não esteja na "direção padrão" do seu idioma, e expressões "intermediárias" que misturam os dois definitivamente devem ser evitadas.
O operador de canal do F #
|>
é útil em parte porque permite que você escreva da esquerda para a direita coisas que de outra forma teriam que ser da direita para a esquerda.fonte
myCollection.Select(_ => _.SomeProp).Where(_ => _.Size > 4);
Eu diria que não, porque você deve ler o "menor escopo possível" como "entre escopos existentes ou razoáveis para adicionar". Caso contrário, isso implicaria que você deveria criar escopos artificiais (por exemplo,
{}
blocos gratuitos em linguagens do tipo C) apenas para garantir que o escopo de uma variável não se estenda além do último uso pretendido e que geralmente seria desaprovado como ofuscação / confusão, a menos que já exista uma boa razão para o escopo existir independentemente.fonte
Considere funções ( métodos ). Lá, ele não está dividindo o código na menor subtarefa possível, nem o maior pedaço de código único.
É um limite de mudança, com a delimitação de tarefas lógicas, em partes consumíveis.
O mesmo vale para variáveis . Apontando estruturas lógicas de dados, em partes compreensíveis. Ou também simplesmente nomear (declarando) os parâmetros:
Mas é claro que, tendo uma declaração no topo, e duzentas linhas a mais, o primeiro uso é hoje aceito como mau estilo. É claramente o que "variáveis devem viver no menor escopo possível" pretende dizer. Como um muito próximo "não reutilize variáveis".
fonte
O que está faltando como motivo para NÃO é a depuração / capacidade de leitura. O código deve ser otimizado para isso, e nomes claros e concisos ajudam muito, por exemplo, imagine uma maneira de se
esta linha é curta, mas já é difícil de ler. Adicione mais alguns parâmetros, e isso se estender por várias linhas.
Acho esse caminho mais fácil de ler e comunicar significado - por isso não tenho problemas com variáveis intermediárias.
Outro exemplo seria idiomas como R, onde a última linha é automaticamente o valor de retorno:
isso é perigoso, o retorno é extinto ou necessário? isso é mais claro:
Como sempre, esta é uma decisão judicial - elimine variáveis intermediárias se elas não melhorarem a leitura, caso contrário, mantenha ou introduza-as.
Outro ponto pode ser a depuração: se os resultados do intermediário forem interessantes, pode ser melhor simplesmente introduzir um intermediário, como no exemplo R acima. A frequência com que isso é solicitado é difícil de imaginar e tome cuidado com o check-in - muitas variáveis de depuração são confusas - novamente, uma decisão judicial.
fonte
Referindo-se apenas ao seu título: absolutamente, se uma variável for desnecessária, ela deverá ser excluída.
Mas “desnecessário” não significa que um programa equivalente possa ser escrito sem o uso da variável; caso contrário, seríamos informados de que deveríamos escrever tudo em binário.
O tipo mais comum de variável desnecessária é uma variável não utilizada, quanto menor o escopo da variável, mais fácil é determinar que ela é desnecessária. Se uma variável intermediária é desnecessária, é mais difícil determinar, porque não é uma situação binária, é contextual. De fato, código-fonte idêntico em dois métodos diferentes pode produzir uma resposta diferente pelo mesmo usuário, dependendo da experiência anterior em corrigir problemas no código ao redor.
Se o seu código de exemplo estivesse exatamente como representado, eu sugeriria se livrar dos dois métodos privados, mas teria pouca ou nenhuma preocupação sobre se você salvou o resultado das chamadas de fábrica em uma variável local ou apenas os usou como argumentos para a mistura método.
A legibilidade do código supera tudo, exceto o trabalho correto (inclui corretamente critérios de desempenho aceitáveis, que raramente são "o mais rápido possível").
fonte