O que define código robusto?


42

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"?



4
Isso seria válido apenas em um idioma não fortemente tipado. Em uma linguagem fortemente digitado uma variável do tipo boolean (não algum posando inteiro como um boolean), só pode ser verdadeira ou falsa, não há terceira opção ...
Marjan Venema

23
pergunte a ele como você testaria a cobertura no terceiro caso, porque o código robusto certamente exigirá testes e, se você não conseguir testar o terceiro caso, não poderá encontrar bugs que possam estar lá.
precisa saber é o seguinte

2
@Marjan - em uma língua não-fortemente tipado provavelmente apenas escrever: if (var) {} else {}
kevin Cline

2
Não conheço nenhum idioma em que x e! X possam ser verdadeiros. Note que eu não sugeri "if (x == true) ..."; Eu abomino essas comparações.
kevin Cline

Respostas:


33

qual é o sentido de verificar um terceiro estado quando um terceiro estado é logicamente impossível?

Que tal um Boolean?que permite um NULLestado 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 dados Booleane inicializar os dados atuais NULLinicialmente? 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.


13
Se esse fosse um booleano anulável, tanto Java quanto c # seriam lançados, portanto nulo deve ser verificado primeiro.
Esben Skov Pedersen

Parece não haver uma definição universalmente aceita do que é um gato ou um cachorro.
Tulains Córdova

11

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.

if (var == true) {
    ...
} else if (var == false) {
    ...
} else {
    ...
}

Opção B

if (var == true) {
    ...
} else {
    ...
}

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.

if (var != true || var != false) {
    errorReport("Hell just froze over, var must be true or false")
}
......
if (var == true){
 .... 
} else {
 .... 
}

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)


1
".. erros inesperados. Eles geralmente são fáceis de serem detectados quando você pensa neles" - mas quando você pensa neles, eles não são mais inesperados.
precisa saber é o seguinte

7
alguma dúvida sobre se o seu código if (var != true || var != false) {deve ser um &&.

1
Posso facilmente pensar em um bool que não seja verdadeiro nem falso, mas ainda é inesperado. Caso você diga que um bool não pode ser outra coisa, se eu verificar se uma letra é um caractere de dígito e convertê-lo em seu valor inteiro, posso pensar facilmente que esse valor inteiro é menor que 0 ou maior que 9, mas ainda é inesperado.
gnasher729

1
Booleanos nulos são suportados em Java e C # e possuem um aplicativo do mundo real. Considere um banco de dados contendo uma lista de pessoas. Depois de um tempo, você decide que precisa de um campo de gênero (isMale). Nulo significa "nunca perguntei, então não saiba"; verdadeiro significa masculino e falso significa feminino. (OK, transgênero omitido por simplicidade ...).
Kiwiron

@kiwiron: Uma solução melhor seria não usar um tipo de enumeração, "Masculino", "Feminino", "Não Perguntei". As enumerações são melhores - podem ser estendidas quando surgir a necessidade (por exemplo: Assexual, Hermafrodita, "Recusou-se a responder").
mattnz

9

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.


Eu não entendo em naves espaciais é proibido o código morto? ou seja, você não pode escrever o último mais? desde que você não pode testá-lo, você não pode colocá-lo? mas então o que significa "Se o SEU ativar uma parte do código robusto, o comportamento (inesperado) permanecerá sob controle".
Pobre

5
Sim, a cobertura de teste de software crítico no espaço deve ser 100% e, como consequência, o código inacessível (também conhecido como código morto) é proibido.
Mouviciel

7

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.

// Assign 3 to x
var x = 3;
x = 3;   // again, just for sure
while (x < 3 or x > 3) { x = 3; }  // being robust
if (x != 3) { ... }  // this got to be an error!

1
O último, se certamente for acionado, não exigiria muito esforço. Qualquer programador C experiente viu valores mudarem repentinamente. Logicamente, em um ambiente controlado de thread único, isso nunca deve acontecer. Na vida real, o código dentro do if eventualmente acontecerá. Se não houver nada de útil que você possa fazer nesse caso, não o codifique! (Tive uma experiência engraçada durante um desenvolvimento de software específico, onde levantei uma exceção com palavrões, caso algo impossível acontecesse ... adivinhe o que aconteceu?).
24416 Alex

2
História verdadeira:boolean x = something(); if (x) { x = True // make sure it's really true, ... }
Andres F.

6

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:

  • Em Haskell, a Booleanpode ser um Trueou False, não há terceira opção
  • Em C ++, a boolpode ser true, falseou (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:

  • Estabeleça invariantes para as classes (por exemplo, sizeé o número de itens na datalista)
  • Estabeleça pré-condições e pós-condições para cada função (por exemplo, essa função só pode ser chamada com amenos de 10)
  • Teste cada um dos pontos de entrada e saída de cada uma das suas funções

Exemplo:

class List:
  def __init__(self, items):
    self.__size = len(items)
    self.__data = items

  def __invariant(self):
    assert self.__size == len(self.__data)

  def size(self):
    self.__invariant()

    return self.__size

  def at(self, index):
    """index should be in [0,size)"""
    self.__invariant()
    assert index >= 0 and index < self.__size

    return self.__data[index]

  def pushback(self, item):
    """the subsequent list is one item longer
       the item can be retrieved by self.at(self.size()-1)"""
    self.__invariant()

    self.__data.append(item)
    self.__size += 1

    self.__invariant()
    assert self.at(self.size()-1) == item

Mas o professor disse especificamente que era Java, e NÃO disse especificamente qual é o tipo de var. Se for booleano, pode ser verdadeiro, falso ou nulo. Se algo mais, pode ser desigual tanto para verdadeiro quanto desigual para falso. Sim, se sobrepõem entre robusto, defensivo e paranóico.
Andy Canfield

2
Em C, C ++ e Objective-C, um bool pode ter um valor indeterminado, como qualquer outro tipo, mas qualquer atribuição o definirá como verdadeiro ou falso e nada mais. Por exemplo: bool b = 0; b ++; b ++; definirá b como verdadeiro.
gnasher729

2

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:

Spec: If var is false then the function does "this", otherwise it does "that". 

Então você escreve a função:

if (var == false) dothis; else dothat; 

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.


1

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.


0

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.


0

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:

for (int i = 0; i != sequence.length(); ++i) {
    // do something with sequence[i]
}

Ou:

for (int i = 0; i < sequence.length(); ++i) {
    // do something with sequence[i]
}

(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 que iserá incrementada apenas uma vez por iteração. Se pular o valor sequence.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 que inunca é 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 ipara 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á incrementado ie o loop continuará além do final da sequência.


-1

Pessoalmente, descrevo um código como 'robusto', com este, atributos importantes:

  1. Se minha mãe se senta na frente dela e trabalha com ela, ela não pode quebrar o sistema

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.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.