Meu chefe continua mencionando indiferentemente que maus programadores usam break
e continue
em loops.
Eu os uso o tempo todo porque fazem sentido; deixe-me mostrar a inspiração:
function verify(object) {
if (object->value < 0) return false;
if (object->value > object->max_value) return false;
if (object->name == "") return false;
...
}
O ponto aqui é que primeiro a função verifica se as condições estão corretas e depois executa a funcionalidade real. O mesmo se aplica aos loops da IMO:
while (primary_condition) {
if (loop_count > 1000) break;
if (time_exect > 3600) break;
if (this->data == "undefined") continue;
if (this->skip == true) continue;
...
}
Eu acho que isso facilita a leitura do & debug; mas também não vejo uma desvantagem.
goto
) são úteis em alguns casos.Respostas:
Quando usados no início de um bloco, como as primeiras verificações feitas, eles agem como pré-condições, por isso é bom.
Quando usados no meio do bloco, com algum código ao redor, eles agem como armadilhas ocultas, então é ruim.
fonte
Você pode ler o artigo de Donald Knuth sobre Programação Estruturada, de 1974 , em Statements , no qual ele discute vários usos dos
go to
estruturalmente desejáveis. Eles incluem o equivalente debreak
econtinue
instruções (muitos dos usos dego to
lá foram desenvolvidos em construções mais limitadas). Seu chefe é do tipo que chama Knuth de um mau programador?(Os exemplos dados me interessam. Normalmente,
break
econtinue
não são apreciados por pessoas que gostam de uma entrada e uma saída de qualquer parte do código, e esse tipo de pessoa também desaprova váriasreturn
declarações.)fonte
Break
,Continue
eExit
como ferramentas em minha caixa de ferramentas; Eu os uso onde eles facilitam o acompanhamento do código e não os uso onde tornaria as coisas mais difíceis de ler.Eu não acredito que eles são ruins. A idéia de que eles são ruins vem dos dias da programação estruturada. Está relacionado à noção de que uma função deve ter um único ponto de entrada e um único ponto de saída, ou seja, apenas um
return
por função.Isso faz algum sentido se sua função for longa e se você tiver vários loops aninhados. No entanto, suas funções devem ser curtas e você deve agrupar loops e seus corpos em funções curtas próprias. Geralmente, forçar uma função a ter um único ponto de saída pode resultar em lógica muito complicada.
Se sua função for muito curta, se você tiver um único loop ou, no máximo, dois loops aninhados, e se o corpo do loop for muito curto, ficará muito claro o que um
break
ou umcontinue
faz. Também está claro o que váriasreturn
declarações fazem.Esses problemas são abordados em "Código Limpo", de Robert C. Martin e em "Refatoração", de Martin Fowler.
fonte
CALL P(X, Y, &10)
e, em caso de erro, a função poderia passar o controle para essa instrução, em vez de retornar ao ponto da chamada.Os programadores ruins falam em absolutos (assim como Sith). Bons programadores usam a solução mais clara possível ( todas as outras coisas são iguais ).
Usar break e continue com frequência dificulta o código. Mas se a substituição deles tornar o código ainda mais difícil de seguir, será uma mudança ruim.
O exemplo que você deu é definitivamente uma situação em que os intervalos e continuações devem ser substituídos por algo mais elegante.
fonte
A maioria das pessoas pensa que é uma má ideia porque o comportamento não é facilmente previsível. Se você está lendo o código e vê
while(x < 1000){}
que presume que ele funcionará até x> = 1000 ... Mas se houver interrupções no meio, isso não será verdadeiro, então você não pode realmente confiar no seu looping ...É a mesma razão pela qual as pessoas não gostam do GOTO: com certeza, ele pode ser usado bem, mas também pode levar a um código espaguete, onde o código salta aleatoriamente de seção para seção.
Para mim, se eu fosse fazer um loop que quebrasse em mais de uma condição, eu
while(x){}
alternaria X para false quando precisasse sair. O resultado final seria o mesmo, e qualquer pessoa que lesse o código saberia examinar mais de perto as coisas que alteravam o valor de X.fonte
while(notDone){ }
abordagem.break;
porx=false;
não torna seu código mais claro. Você ainda precisa procurar essa afirmação no corpo. E no caso dex=false;
você ter que verificar se não estáx=true;
mais longe.while (x < 1000)
que suponho que ele será executado 1000 vezes". Bem, há muitas razões pelas quais isso é falso, mesmo quex
seja inicialmente zero. Por exemplo, quem disse quex
é incrementado precisamente uma vez durante o loop e nunca modificado de outra maneira? Mesmo para sua própria suposição, apenas porque algo é definidox >= 1000
não significa que o loop será encerrado - ele pode voltar ao intervalo antes que a condição seja verificada.Sim, você pode [re] escrever programas sem instruções de interrupção (ou retorna do meio dos loops, que fazem a mesma coisa). Mas pode ser necessário introduzir variáveis adicionais e / ou duplicação de código, as quais normalmente dificultam a compreensão do programa. Pascal (a linguagem de programação) era muito ruim, especialmente para programadores iniciantes por esse motivo. Seu chefe basicamente quer que você programe nas estruturas de controle de Pascal. Se Linus Torvalds estivesse no seu lugar, ele provavelmente mostraria ao seu chefe o dedo médio!
Existe um resultado de ciência da computação chamado hierarquia de estruturas de controle de Kosaraju, que remonta a 1973 e é mencionado no famoso artigo de Knuth (mais) sobre gotos de 1974. (Este artigo de Knuth já foi recomendado acima por David Thornley, a propósito) .) O que S. Rao Kosaraju provou em 1973 é que não é possível reescrever todos os programas com quebras de profundidade em vários níveis n em programas com profundidade de quebra menor que n sem a introdução de variáveis extras. Mas digamos que seja apenas um resultado puramente teórico. (Basta adicionar algumas variáveis extras ?! Certamente você pode fazer isso para agradar seu chefe ...)
O que é muito mais importante do ponto de vista da engenharia de software é um artigo mais recente de Eric S. Roberts, de 1995, intitulado Saídas em loop e programação estruturada: reabrindo o debate ( http://cs.stanford.edu/people/eroberts/papers/SIGCSE- 1995 / LoopExits.pdf ). Roberts resume vários estudos empíricos conduzidos por outros antes dele. Por exemplo, quando um grupo de estudantes do tipo CS101 foi convidado a escrever código para uma função que implementava uma pesquisa seqüencial em uma matriz, o autor do estudo disse o seguinte sobre os alunos que usaram um intervalo / retorno / ir para sair do o loop de pesquisa sequencial quando o elemento foi encontrado:
Roberts também diz que:
Sim, você pode ser mais experiente do que os alunos do CS101, mas sem usar a instrução break (ou retornar / sair equivalentemente do meio dos loops), eventualmente você escreverá um código que, embora seja bem estruturado nominalmente, seja cabeludo o suficiente em termos de lógica extra variáveis e duplicação de código que alguém, provavelmente você mesmo, colocará erros de lógica ao tentar seguir o estilo de codificação do seu chefe.
Também vou dizer aqui que o artigo de Roberts é muito mais acessível para o programador médio, para uma leitura melhor do que a de Knuth. Também é mais curto e cobre um tópico mais restrito. Você provavelmente poderia até recomendar ao seu chefe, mesmo que ele seja o gerente e não o tipo de CS.
fonte
Não considero usar nenhuma dessas práticas ruins, mas usá-las demais no mesmo loop deve garantir uma repensar a lógica usada no loop. Use-os com moderação.
fonte
O exemplo que você deu não precisa de pausas nem continua:
Meu 'problema' com as 4 linhas do seu exemplo é que elas estão todas no mesmo nível, mas fazem coisas diferentes: algumas quebras, outras continuam ... Você precisa ler cada linha.
Na minha abordagem aninhada, quanto mais profundo você for, mais 'útil' o código se tornará.
Mas, se no fundo você encontrasse um motivo para interromper o loop (que não seja a condição primária), uma quebra ou retorno teria seu uso. Eu preferiria isso do que usar um sinalizador extra que deve ser testado na condição de nível superior. O intervalo / retorno é mais direto; é melhor indicar a intenção do que definir mais uma variável.
fonte
<
comparações precisa ser<=
para combinar a solução POA "maldade" depende de como você as usa. Normalmente, uso SOMENTE interrupções nas construções de loop quando isso me salva de ciclos que não podem ser salvos através da refatoração de um algoritmo. Por exemplo, percorrer uma coleção procurando um item com um valor em uma propriedade específica definida como true. Se tudo o que você precisa saber é que um dos itens teve essa propriedade configurada como verdadeira, quando você atingir esse resultado, é bom fazer uma pausa para finalizar o loop adequadamente.
Se o uso de uma interrupção não tornar o código mais fácil de ler, mais curto para executar ou salvar ciclos de processamento de maneira significativa, é melhor não usá-los. Costumo codificar para o "menor denominador comum" sempre que possível, para garantir que qualquer pessoa que me siga possa olhar facilmente para o meu código e descobrir o que está acontecendo (nem sempre sou bem-sucedido nisso). As quebras reduzem isso porque introduzem pontos de entrada / saída ímpares. Quando mal utilizados, eles podem se comportar como uma declaração "goto" fora de sintonia.
fonte
Absolutamente não ... Sim, o uso de
goto
é ruim porque deteriora a estrutura do seu programa e também é muito difícil entender o fluxo de controle.Mas o uso de declarações como
break
econtinue
é absolutamente necessário hoje em dia e não é considerado uma má prática de programação.E também não é tão difícil de entender o fluxo de controle no uso de
break
econtinue
. Em construções comoswitch
abreak
afirmação é absolutamente necessária.fonte
A noção essencial vem de poder analisar semanticamente seu programa. Se você tiver uma única entrada e uma única saída, a matemática necessária para indicar possíveis estados é consideravelmente mais fácil do que se você tiver que gerenciar caminhos de bifurcação.
Em parte, essa dificuldade reflete a possibilidade de raciocinar conceitualmente sobre seu código.
Francamente, seu segundo código não é óbvio. O que isso está fazendo? Continua 'continua' ou 'segue' o loop? Eu não faço ideia. Pelo menos o seu primeiro exemplo é claro.
fonte
Eu substituiria seu segundo trecho de código por
não por qualquer motivo de concisão - na verdade, acho que é mais fácil ler e alguém entender o que está acontecendo. De um modo geral, as condições para seus loops devem estar contidas apenas nas condições de loop que não estão espalhadas por todo o corpo. No entanto, existem algumas situações em que
break
econtinue
podem ajudar na legibilidade.break
mais do quecontinue
eu poderia acrescentar: Dfonte
foreach
loop, pois isso itera todos os itens de uma coleção. Umfor
loop é semelhante, pois não deve ter um ponto final condicional. Se você precisar de um ponto final condicional, precisará usar umwhile
loop.break
na minha opinião , deve haver apenas um máximo.Eu discordo do seu chefe. Existem locais apropriados para
break
econtinue
serem usados. De fato, a razão pela qual as exceções e o tratamento de exceções foram introduzidos nas linguagens de programação modernas é que você não pode resolver todos os problemas usando apenasstructured techniques
.Em uma nota lateral , não quero iniciar uma discussão religiosa aqui, mas você pode reestruturar seu código para ficar ainda mais legível assim:
Em outra nota lateral
Pessoalmente, não gosto do uso de
( flag == true )
in condicionais, porque se a variável já é booleana, você está introduzindo uma comparação adicional que precisa acontecer quando o valor do booleano tiver a resposta que você deseja - a menos que você tenha certeza de que seu compilador otimizar essa comparação extra de distância.fonte
boolean
é ou o que a terminologia concisa / elegante significa é, talvez seja melhor contratar alguns mantenedores mais inteligentes ;-)Eu concordo com o seu chefe. Eles são ruins porque produzem métodos com alta complexidade ciclomática. Tais métodos são difíceis de ler e difíceis de testar. Felizmente, há uma solução fácil. Extraia o corpo do loop em um método separado, onde o "continue" se tornará "retorno". "Retorno" é melhor porque depois que o "retorno" acaba - não há preocupações com o estado local.
Para "break", extraia o próprio loop em um método separado, substituindo "break" por "return".
Se os métodos extraídos exigirem um grande número de argumentos, isso é uma indicação para extrair uma classe - colete-os em um objeto de contexto.
fonte
Eu acho que é apenas um problema quando aninhado profundamente dentro de vários loops. É difícil saber em qual loop você está quebrando. Pode ser difícil seguir uma continuação também, mas acho que a verdadeira dor vem das quebras - a lógica pode ser difícil de seguir.
fonte
break 2
; para outros, eu acho bandeiras bool temporários são usadosContanto que eles não sejam usados como saltos disfarçados, como no exemplo a seguir:
Eu estou bem com eles. (Exemplo visto no código de produção, meh)
fonte
Eu não gosto de nenhum desses estilos. Aqui está o que eu preferiria:
Eu realmente não gosto de usar
return
para abortar uma função. Parece um abuso dereturn
.O uso
break
também nem sempre é claro para a leitura.Melhor ainda pode ser:
menos aninhamento e as condições complexas são refatoradas em variáveis (em um programa real, você teria que ter nomes melhores, obviamente ...)
(Todo o código acima é pseudo-código)
fonte
break
econtinue
. Anormalmente, terminar um loop parece estranho e eu não gosto.continue
significa pular a funcionalidade, vá para o próximo loop; não "continue com a execução"Não. É uma maneira de resolver um problema e há outras maneiras de resolvê-lo.
Muitas linguagens mainstream atuais (Java, .NET (C # + VB), PHP, escreva você mesmo) usam "break" e "continue" para pular loops. Ambos sentenciaram "goto (s) estruturados".
Sem eles:
Com eles:
Observe que o código "break" e "continue" é mais curto e geralmente transforma "while" sentenças em sentenças "for" ou "foreach".
Ambos os casos são uma questão de estilo de codificação. Eu prefiro não usá-los , porque o estilo detalhado me permite ter mais controle do código.
Na verdade, eu trabalho em alguns projetos, onde era obrigatório usar essas frases.
Alguns desenvolvedores podem pensar que não são necesarilly, mas hipotéticos, se tivéssemos que removê-los, temos que remover "while" e "do while" ("repita até", pessoal do pascal) também ;-)
Conclusão, mesmo se eu preferir não usá-los, acho que é uma opção, não uma má prática de programação.
fonte
Eu não sou contra
continue
ebreak
em princípio, mas acho que são construções de nível muito baixo que muitas vezes podem ser substituídas por algo ainda melhor.Estou usando C # como exemplo aqui, considere o caso de querer iterar sobre uma coleção, mas queremos apenas os elementos que atendem a algum predicado e não queremos fazer mais do que 100 iterações.
Isso parece razoavelmente limpo. Não é muito difícil de entender. Eu acho que valeria muito a pena ser mais declarativo. Compare com o seguinte:
Talvez as chamadas Onde e Receber não devam estar nesse método. Talvez essa filtragem deva ser feita ANTES que a coleção seja passada para esse método. De qualquer forma, afastando-se de coisas de baixo nível e concentrando-se mais na lógica de negócios real, fica mais claro o que realmente interessa. Torna-se mais fácil separar nosso código em módulos coesos que aderem mais às boas práticas de design e, portanto, em.
Ainda existem coisas de baixo nível em algumas partes do código, mas queremos ocultar o máximo possível, porque é preciso energia mental que poderíamos estar usando para raciocinar sobre os problemas de negócios.
fonte
O Code Complete tem uma boa seção sobre o uso
goto
e vários retornos da rotina ou loop.Em geral, não é uma prática ruim.
break
oucontinue
diga exatamente o que acontece a seguir. E eu concordo com isso.Steve McConnell (autor do Code Complete) usa quase os mesmos exemplos que você para mostrar vantagens de usar várias
goto
instruções.No entanto, o uso excessivo
break
oucontinue
pode levar a software complexo e insustentável.fonte