Quais etapas e medidas posso tomar para evitar recuos profundos no meu código?
Quais etapas e medidas posso tomar para evitar recuos profundos no meu código?
Respostas:
O recuo profundo geralmente não é um problema se todas as funções / métodos do seu programa fizerem uma e apenas uma coisa. Ocasionalmente, pode ser necessário aninhar condicionais com alguns níveis de profundidade, mas posso dizer honestamente que só escrevi código recuado profundamente algumas vezes em mais de 12 anos de codificação.
A melhor coisa que você pode fazer é extrair métodos:
int Step1(int state)
{
if (state == 100)
{
return Step2(state);
}
else
{
return Step3(state);
}
}
int Step2(int state)
{
if (state != 100)
{
throw new InvalidStateException(2, state);
}
// ....
}
if
. Levado ao extremo, você terminará com pseudocódigo executável.
else
blocos desnecessários .
Talvez você possa considerar cláusulas de guarda ?
ao invés de
public void DoSomething(int value){
if (someCondition){
if(someOtherCondition){
if(yetAnotherCondition){
//Finally execute some code
}
}
}
}
Faz
public void DoSomething(int value){
if(!(someCondition && someOtherCondition && yetAnotherCondition)){
return;
//Maybe throw exception if all preconditions must be true
}
//All preconditions are safe execute code
}
Se você tiver uma chance, eu recomendo que você leia Code Complete, de Steve McConnell. Ele tem muitos conselhos excelentes sobre esses tópicos.
http://www.amazon.com/Code-Complete-Practical-Handbook-Construction/dp/0735619670/ref=pd_sim_b_6
Para obter mais informações sobre "cláusulas de guarda", consulte: https://sourcemaking.com/refactoring/replace-nested-conditional-with-guard-clauses
Inverta seus if
s.
Ao invés de:
if (foo != null)
{
something;
something;
if (x)
{
something;
}
something;
}
else
{
boohoo;
}
Eu escreveria:
if (foo == null)
{
boohoo;
return;
}
something;
something;
if (x)
{
something;
}
something;
O mesmo se aplica a if
- else
blocos. Se else
for mais curto / menos aninhado, revertê-los.
Verifique os valores dos parâmetros em um só lugar
Verifique todos os parâmetros em busca de valores ilegais assim que você inserir seu método e continue sabendo que está seguro. Isso cria um código mais legível, mas também evita que você empilhe blocos condicionais posteriormente e espalhe essas verificações por toda a sub-rotina.
If
s no início do código que interrompe o fluxo de execução devido a alguma condição não ser atendida também são conhecidos como cláusulas de salvaguarda , como apontou @JasonTuran. E isso parece ser o mais próximo possível de ter um nome distinto.
Normalmente, vi que o código profundamente recuado é geralmente um código problemático. Se você estiver enfrentando esse problema, dê um passo atrás e avalie se sua função está fazendo muitas coisas.
Ao mesmo tempo, para responder sua pergunta, se houver uma necessidade de indentação tão profunda, sugiro que você a deixe lá. Pela simples razão de que, em tal código, o recuo ajudará, pois é provável que seja um trecho muito longo.
Divida os componentes aninhados (especialmente os repetidos) em funções separadas (isso é mais fácil se o seu idioma suportar fechamentos) ou substitua uma série de loops aninhados por uma recursão.
Recue também dois espaços em vez de quatro.
Não vejo indentações profundas como um problema categórico a ser removido (nem vejo a refatoração como a verdadeira resposta para tudo).
Normalmente, em vez de ifs aninhados, gosto de escrever instruções lógicas:
if (foo && bar && baz)
ao invés de
if foo
if bar
if baz
Eu mesmo não acreditei, mas, de acordo com o Code Complete, este é um local apropriado para usar break
(se sua equipe estiver a bordo). Eu imagino que isso seja mais aceitável com programadores C ++, onde é break
usado em switch
declarações do que com programadores Delphi, onde break
é usado somente quando você não deseja escrever um while
loop.
Recuo é realmente um pensamento para lutar, de fato. O que eu aprendi a fazer é dividir o método em partes primeiro e depois usar um truque estranho para pular todas as partes seguintes, se uma delas falhar. Aqui está um exemplo :
Ao invés de :
{if (networkCardIsOn() == true)
{if (PingToServer() == true)
{if (AccesLogin(login,pass) == true)
{if (nextCondition == true)
...
}
}
}
Atualmente, escrevo:
{vbContinue = true;
if (vbContinue) {
vbContinue = networkCardIsOn();
if (vbContinue == false) {
code to Handle This Error();
}
}
if (vbContinue) {
vbContinue = PingToServer();
if (vbContinue == false) {
code to HandleThisError2();
}
}
if (vbContinue) {
vbContinue = AccesLogin(login,pass);
if (vbContinue == false) {
HandleThisErrorToo();
}
}
...
Isso me pareceu estranho no começo, mas desde que eu uso isso, o custo de manutenção foi dividido pela metade e meu cérebro fica mais frio no final do dia.
De fato, o ganho introduzido por essa "técnica" é que a complexidade do código é realmente dividida porque o código é menos denso.
Ao ler o código, você não precisa se lembrar de nada sobre as condições passadas: se você estiver no ponto X do código, as etapas anteriores são aprovadas e foram bem-sucedidas.
Outro ganho é que o "caminho e condição de escape" de todos os aninhados "if-else" é simplificado.