Meu professor continua se referindo a este exemplo de Java quando fala de código "robusto":
if (var == true) {
...
} else if (var == false) {
...
} else {
...
}
Ele afirma que "código robusto" significa que seu programa leva em consideração todas as possibilidades e que não existe erro - todas as situações são tratadas pelo código e resultam em um estado válido, daí o "else".
Eu sou duvidoso, no entanto. Se a variável é booleana, qual é o sentido de verificar um terceiro estado quando um terceiro estado é logicamente impossível?
"Não ter erro" parece ridículo também; até os aplicativos do Google mostram erros diretamente para o usuário, em vez de absorvê-los silenciosamente ou de alguma forma considerá-los como um estado válido. E é bom - eu gosto de saber quando algo dá errado. E parece bastante a afirmação de que um aplicativo nunca apresentaria erros.
Então, qual é a definição real de "código robusto"?
fonte
Respostas:
Que tal um
Boolean?
que permite umNULL
estado que não é verdadeiro nem falso. Agora, o que o software deve fazer? Alguns softwares precisam ser altamente resistentes a falhas, como marca-passos. Já viu alguém adicionar uma coluna a um banco de dadosBoolean
e inicializar os dados atuaisNULL
inicialmente? Eu sei que já vi.Aqui estão alguns links que discutem o que significa ser robusto em termos de software:
Se você acha que há uma definição universalmente aceita de "robusto" aqui, boa sorte. Pode haver alguns sinônimos como à prova de bomba ou à prova de idiotas. O programador de fita adesiva seria um exemplo de alguém que normalmente escreve código robusto, pelo menos na minha compreensão dos termos.
fonte
Para o bem da minha discussão, um Bool pode ter 2 estados, Verdadeiro ou Falso. Qualquer outra coisa não está em conformidade com a especificação da linguagem de programação. Se a sua cadeia de ferramentas não estiver em conformidade com suas especificações, você será mangueira, não importa o que faça. Se um desenvolvedor criou um tipo de Bool com mais de 2 estados, é a última coisa que ele faria na minha base de código.
Opção A.
Opção B
Eu afirmo que a opção B é mais robusta .....
Qualquer twit pode dizer para você lidar com erros inesperados. Eles geralmente são fáceis de detectar de maneira trivalente quando você pensa neles. O exemplo que seu professor deu não é algo que poderia acontecer, por isso é um exemplo muito ruim.
É impossível testar A sem os chicotes de teste complicados. Se você não pode criá-lo, como vai testá-lo? Se você não testou o código, como sabe que funciona? Se você não sabe que funciona, não está escrevendo um software robusto. Eu acho que eles ainda chamam isso de Catch22 (ótimo filme, assista algum dia).
A opção B é trivial para testar.
Próximo problema, pergunte ao professor esta pergunta "O que você quer que eu faça sobre isso se um booleano não é verdadeiro nem falso?" Isso deve levar a uma discussão muito interessante ...
Na maioria dos casos, um dump principal é apropriado, na pior das hipóteses, irrita o usuário ou custa muito dinheiro. E se, digamos, o módulo for o sistema de cálculo de reinscrição em tempo real do ônibus espacial? Qualquer resposta, não importa quão imprecisa, não pode ser pior do que abortar, o que matará os usuários. Então, o que fazer, se você souber que a resposta pode estar errada, vá para o 50/50 ou aborte e vá para a falha de 100%. Se eu fosse um membro da tripulação, eu pegaria a 50/50.
A opção A me mata A opção B me dá uma chance uniforme de sobrevivência.
Mas espere - é uma simulação da reentrada do ônibus espacial - e depois? Abortar para que você saiba sobre isso. Parece uma boa ideia? - NÃO - porque você precisa testar com o código que planeja enviar.
A opção A é melhor para simulação, mas não pode ser implementada. É inútil A opção B é o código implementado, portanto a simulação executa o mesmo que os sistemas ativos.
Digamos que essa era uma preocupação válida. A melhor solução seria isolar o tratamento de erros da lógica do aplicativo.
Outras leituras - Máquina Therac-25 Xray, falha no Ariane 5 Rocket e outras (o link tem muitos links quebrados, mas informações suficientes para o Google ajudar)
fonte
if (var != true || var != false) {
deve ser um&&
.Na verdade, seu código não é mais robusto, mas é menos robusto. A final
else
é simplesmente um código morto que você não pode testar.Em softwares críticos, como naves espaciais, é proibido o código morto e, geralmente, o código não testado: Se um raio cósmico produz uma única perturbação de evento que, por sua vez, ativa o código morto, tudo é possível. Se o SEU ativar uma parte do código robusto, o comportamento (inesperado) permanecerá sob controle.
fonte
Eu acho que o professor pode estar confundindo "erro" e "bug". Código robusto certamente deve ter poucos / nenhum erro. Código robusto pode, e em um ambiente hostil, deve ter um bom gerenciamento de erros (seja manipulação de exceção ou testes rigorosos de status de retorno).
Concordo que o exemplo de código do professor é bobo, mas não tão bobo quanto o meu.
fonte
boolean x = something(); if (x) { x = True // make sure it's really true, ... }
Não existe uma definição acordada de Código Robusto , pois para muitas coisas na programação é mais ou menos subjetiva ...
O exemplo que seu professor dá depende do idioma:
Boolean
pode ser umTrue
ouFalse
, não há terceira opçãobool
pode sertrue
,false
ou (infelizmente) provém de algum elenco dúbio que o coloca em um caso desconhecido ... Isso não deve acontecer, mas pode, como resultado de um erro anterior.No entanto, o que seu professor está aconselhando obscurece o código, introduzindo uma lógica estranha para eventos que não devem acontecer no meio do programa principal. Por isso, vou apontar para a programação defensiva .
No caso da universidade, você pode até aumentá-lo adotando uma estratégia Design By Contract:
size
é o número de itens nadata
lista)a
menos de10
)Exemplo:
fonte
A abordagem do seu professor é totalmente equivocada.
Uma função, ou apenas um pouco de código, deve ter uma especificação que diga o que faz, que deve cobrir todas as entradas possíveis. E o código deve ser escrito para garantir que seu comportamento corresponda às especificações. No exemplo, eu escreveria a especificação bem simples assim:
Então você escreve a função:
e o código atende às especificações. Então, seu professor diz: E se var == 42? Veja a especificação: diz que a função deve fazer "isso". Veja o código: A função faz "isso". A função atende às especificações.
Onde o código do seu professor torna as coisas totalmente não confiáveis, é o fato de que, com a abordagem dele, quando var não é verdadeiro nem falso, ele executa código que nunca foi chamado antes e que é completamente não testado, com resultados totalmente imprevisíveis.
fonte
Concordo com a afirmação de @ gnasher729: a abordagem do seu professor é totalmente equivocada.
Robusto significa que é resistente a quebra / falha porque faz poucas suposições e é dissociado: é autônomo, autodefinitivo e portátil. Também inclui ser adaptável às mudanças nos requisitos. Em uma palavra, seu código é durável .
Isso geralmente se traduz em funções curtas que obtêm seus dados dos parâmetros passados pelo chamador e no uso de interfaces públicas para os consumidores - métodos abstratos, wrappers, indirection, interfaces de estilo COM etc. - em vez de funções que contêm código de implementação concreto.
fonte
Código robusto é simplesmente um código que lida bem com falhas. Nem mais nem menos.
De falhas, existem muitos tipos: código incorreto, código incompleto, valores inesperados, estados inesperados, exceções, exaustão de recursos, .... O código robusto lida com isso muito bem.
fonte
Eu consideraria o código que você deu como um exemplo de programação defensiva (pelo menos como eu uso o termo). Parte da programação defensiva é fazer escolhas que minimizem as suposições feitas sobre o comportamento do resto do sistema. Por exemplo, qual deles é melhor:
Ou:
(Caso esteja tendo problemas para ver a diferença, verifique o teste de loop: os primeiros usos
!=
, os segundos usos<
).Agora, na maioria das circunstâncias, os dois loops se comportarão exatamente da mesma maneira. No entanto, o primeiro (comparando com
!=
) faz uma suposição quei
será incrementada apenas uma vez por iteração. Se pular o valorsequence.length()
, o loop poderá continuar além dos limites da sequência e causar um erro.Portanto, você pode argumentar que a segunda implementação é mais robusta: ela não depende de suposições sobre se o corpo do loop é alterado
i
(nota: na verdade, ele ainda assume quei
nunca é negativo).Para dar alguma motivação para o motivo de você não querer fazer essa suposição, imagine que o loop esteja varrendo uma string, fazendo algum processamento de texto. Você escreve o loop e está tudo bem. Agora, seus requisitos mudam e você decide que precisa oferecer suporte a caracteres de escape na sequência de texto, para alterar o corpo do loop, de modo que, se detectar um caractere de escape (digamos, barra invertida), ele será incrementado
i
para ignorar o caractere imediatamente após o escape. Agora, o primeiro loop possui um bug, porque se o último caractere do texto for uma barra invertida, o corpo do loop será incrementadoi
e o loop continuará além do final da sequência.fonte
Pessoalmente, descrevo um código como 'robusto', com este, atributos importantes:
Agora, com uma pausa, quero dizer colocar o sistema em um estado instável ou causar uma exceção UNANDANDLED . Às vezes, para um conceito simples, você pode fazer uma definição e explicação complexa. Mas eu prefiro definições simples. Os usuários são muito bons em encontrar aplicativos robustos. Se o usuário do seu aplicativo enviar muitas solicitações sobre erros, perda de estado, fluxos de trabalho não intuitivos etc., há algo errado com sua programação.
fonte