¿Qué define el código robusto?

42

Mi profesor sigue refiriéndose a este ejemplo de Java cuando habla de código "robusto":

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

Afirma que "código robusto" significa que su programa tiene en cuenta todas las posibilidades, y que no existe un error: el código maneja todas las situaciones y da como resultado un estado válido, de ahí el "otro".

Sin embargo, tengo dudas. Si la variable es booleana, ¿cuál es el punto de verificar un tercer estado cuando un tercer estado es lógicamente imposible?

"No tener tal cosa como un error" parece ridículo también; incluso las aplicaciones de Google muestran errores directamente al usuario en lugar de tragárselos en silencio o de alguna manera los consideran como estado válido. Y es bueno: me gusta saber cuándo algo sale mal. Y parece bastante afirmativo decir que una aplicación nunca tendría ningún error.

Entonces, ¿cuál es la definición real de "código robusto"?

Lotus Notes
fuente
44
Esto solo se mantendría en un lenguaje no fuertemente tipado. En un lenguaje fuertemente tipado, una variable de tipo booleano (no un entero que se hace pasar por booleano), solo puede ser verdadero o falso, no hay una tercera opción ...
Marjan Venema
23
pregúntele cómo probaría la cobertura en el tercer caso, porque un código robusto seguramente debería requerir pruebas, y si no logra probar el tercer caso, no podrá encontrar ningún error que pueda acechar allí.
gbjbaanb
2
@Marjan: en un lenguaje no muy tipado, uno probablemente escribiría: if (var) {} else {}
kevin cline
2
No conozco ningún lenguaje en el que tanto x como! X puedan ser ciertas. Tenga en cuenta que no sugerí "if (x == true) ..."; Aborrezco tales comparaciones.
Kevin Cline

Respuestas:

33

¿Cuál es el punto de verificar un tercer estado cuando un tercer estado es lógicamente imposible?

¿Qué pasa con un Boolean?que permite un NULLestado que no es ni verdadero ni falso? ¿Ahora qué debe hacer el software? Algunos programas tienen que ser altamente resistentes a los choques como los marcapasos. ¿Alguna vez has visto a alguien agregar una columna a una base de datos que era Booleane inicializar los datos actuales NULLinicialmente? Sé que lo he visto.

Aquí hay algunos enlaces que discuten lo que significa ser robusto en términos de software:

Si cree que hay una definición universalmente acordada de "robusto" aquí, buena suerte. Puede haber algunos sinónimos como a prueba de bombas o a prueba de idiotas. El programador de cinta adhesiva sería un ejemplo de alguien que generalmente escribe código robusto al menos en mi comprensión de los términos.

JB King
fuente
13
Si se tratara de un booleano anulable, tanto Java como c # arrojarían un valor nulo, por lo que debería comprobarse primero.
Esben Skov Pedersen
No parece haber una definición universalmente aceptada de lo que son un gato o un perro.
Tulains Córdova
11

Por el bien de mi discusión, un Bool puede tener 2 estados, verdadero o falso. Cualquier otra cosa es la no conformidad con la especificación langugae de programación. Si su cadena de herramientas no es conforme a su especificación, se le aplicará una manguera sin importar lo que haga. Si un desarrollador crea un tipo de Bool que tiene más de 2 estados, es lo último que haría en mi base de código.

Opcion A.

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

Opcion B

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

Afirmo que la opción B es más robusta .....

Cualquier imbécil puede decirte que manejes errores inesperados. Por lo general, son muy fáciles de detectar una vez que piensas en ellos. El ejemplo que ha dado su profesor no es algo que pueda suceder, por lo que es un ejemplo muy pobre.

A es imposible de probar sin arneses de prueba complicados. Si no puede crearlo, ¿cómo lo va a probar? Si no ha probado el código, ¿cómo sabe que funciona? Si no sabe que funciona, entonces no está escribiendo un software robusto. Creo que todavía lo llaman Catch22 (Gran película, mírala alguna vez).

La opción B es trivial para probar.

Siguiente problema, pregúntele al profesor esta pregunta "¿Qué quiere que haga si un booleano no es verdadero ni falso?" Eso debería conducir a una discusión muy interesante .....

En la mayoría de los casos, un volcado de núcleo es apropiado, en el peor de los casos molesta al usuario o cuesta mucho dinero. ¿Qué sucede si, por ejemplo, el módulo es el sistema de cálculo de reentrada en tiempo real del transbordador espacial? Cualquier respuesta, por inexacta que sea, no puede ser peor que abortar, lo que matará a los usuarios. Entonces, qué hacer, si sabe que la respuesta puede ser incorrecta, elija 50/50, o aborte y vaya al 100% de falla. Si fuera un miembro de la tripulación, tomaría el 50/50.

La opción A me mata La opción B me da una posibilidad de supervivencia.

Pero espera, es una simulación de la reentrada del transbordador espacial, ¿entonces qué? Abortar para que lo sepas. ¿Suena como una buena idea? - NO - porque necesita probar con el código que planea enviar.

La opción A es mejor para la simulación, pero no se puede implementar. Es inútil La opción B es el código implementado, por lo que la simulación funciona igual que los sistemas en vivo.

Digamos que esto era una preocupación válida. La mejor solución sería aislar el manejo de errores de la lógica de la aplicación.

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

Lectura adicional : máquina de rayos X Therac-25, falla del cohete Ariane 5 y otros (Link tiene muchos enlaces rotos pero suficiente información que Google ayudará)

Mattnz
fuente
1
"... errores inesperados. Por lo general, son fáciles de detectar una vez que piensas en ellos", pero cuando piensas en ellos, ya no son inesperados.
gbjbaanb
77
Hay alguna pregunta sobre si su código if (var != true || var != false) {debería ser un &&lugar.
1
Puedo pensar fácilmente en un bool que no es ni verdadero ni falso, pero aún así es inesperado. En caso de que diga que un bool no puede ser otra cosa, si verifico si una letra es un carácter de dígito y luego la convierto a su valor entero, puedo pensar fácilmente que ese valor entero es menor que 0 o mayor que 9, pero sigue siendo inesperado.
gnasher729
1
Los booleanos nulos son compatibles con Java y C #, y tienen una aplicación del mundo real. Considere una base de datos que contiene una lista de personas. Después de un tiempo, decide que necesita un campo de género (isMale). Nulo significa "nunca preguntado, así que no sé"; verdadero significa masculino y falso significa femenino. (OK, transgénero omitido por simplicidad ...).
kiwiron
@kiwiron: ¿Sería una mejor solución no utilizar un tipo de enumeración, "Masculino", "Femenino", "No preguntó". Las enumeraciones son mejores: pueden extenderse cuando surja la necesidad (por ejemplo, en su asexualidad, hermafrodita, "se niega a responder").
mattnz
9

En realidad, su código no es más robusto sino MENOS robusto. El final elsees simplemente un código muerto que no puedes probar.

En el software crítico, como en las naves espaciales, está prohibido el código muerto y, en general, el código no probado: si un rayo cósmico produce un solo evento molesto que a su vez hace que se active su código muerto, todo es posible. Si el SEU activa una porción de código robusto, el comportamiento (inesperado) permanece bajo control.

Mouviciel
fuente
No lo entiendo en naves espaciales ¿está prohibido el código muerto? es decir, no puedes escribir el último? ya que no puedes probarlo, ¿no puedes ponerlo? pero entonces, ¿qué significa "si el SEU activa una porción de código robusto, el comportamiento (inesperado) permanece bajo control".
lamentable
55
Sí, en el espacio, la cobertura de prueba de software crítica debe ser del 100% y, en consecuencia, el código inalcanzable (también conocido como código muerto) está prohibido.
mouviciel
7

Creo que el profesor podría estar confundiendo "error" y "error". El código robusto debería tener pocos / ningún error. El código robusto puede, y en un entorno hostil, debe tener una buena gestión de errores (ya sea manejo de excepciones o rigurosas pruebas de estado de retorno).

Estoy de acuerdo en que el código de ejemplo del profesor es tonto, pero no tan tonto como el mío.

// 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!
David Andersson
fuente
1
El último si ciertamente se activa, no requeriría realmente tanto esfuerzo. Cualquier programador de C experimentado ha visto que los valores cambian repentinamente. Por supuesto, lógicamente, en un entorno controlado de un solo subproceso, esto nunca debería suceder. En la vida real, el código dentro del if eventualmente sucederá. Si no hay nada útil que pueda hacer dentro de eso, ¡entonces no lo codifique! (Tuve una experiencia divertida durante un desarrollo de software en particular donde planteé una excepción con palabras de maldición en caso de que sucediera algo imposible ... ¿adivina qué pasó?).
Alex
2
Historia verdadera:boolean x = something(); if (x) { x = True // make sure it's really true, ... }
Andres F.
6

No existe una definición acordada de Código Robusto , ya que para muchas cosas en la programación es más o menos subjetivo ...

El ejemplo que da tu profesor depende del idioma:

  • En Haskell, a Booleanpuede ser cualquiera Trueo Falseno hay una tercera opción
  • En C ++, un boolpuede ser true, falseo (desafortunadamente) venir de un elenco dudoso que lo puso en un caso desconocido ... Esto no debería suceder, pero puede, como resultado de un error anterior.

Sin embargo, lo que su profesor está aconsejando oscurece el código al introducir una lógica extraña para eventos que no deberían suceder en el medio del programa central, por lo que le indicaré, en cambio, hacia la Programación Defensiva .

En el caso de la universidad, incluso podría aumentarlo adoptando una estrategia de diseño por contrato:

  • Establecer invariantes para las clases (por ejemplo, sizees el número de elementos en la datalista)
  • Establezca condiciones previas y posteriores para cada función (por ejemplo, esta función solo puede invocarse con aun valor menor que 10)
  • Pruebe cada uno de los puntos de entrada y salida de cada una de sus funciones.

Ejemplo:

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
Matthieu M.
fuente
Pero el profesor dijo específicamente que era Java, y específicamente NO dijo cuál es el tipo de var. Si es booleano, puede ser verdadero, falso o nulo. Si algo más, puede ser desigual tanto a verdadero como desigual a falso. Sí, superposición entre robusto, defensivo y paranoico.
Andy Canfield
2
En C, C ++ y Objective-C, un bool puede tener un valor indeterminado, como cualquier otro tipo, pero cualquier asignación lo establecerá en verdadero o falso y nada más. Por ejemplo: bool b = 0; b ++; b ++; establecerá b en verdadero.
gnasher729
2

El enfoque de tu profesor es totalmente equivocado.

Una función, o solo un poco de código, debe tener una especificación que diga lo que hace, que debe cubrir cada entrada posible. Y el código debe escribirse para garantizar que su comportamiento coincida con la especificación. En el ejemplo, escribiría la especificación bastante simple como esta:

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

Luego escribes la función:

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

y el código cumple con las especificaciones. Entonces su profesor dice: ¿Qué pasa si var == 42? Mire la especificación: dice que la función debería hacer "eso". Mire el código: la función hace "eso". La función cumple con las especificaciones.

Donde el código de su profesor hace que las cosas sean totalmente descortezadas es el hecho de que con su enfoque, cuando var no es ni verdadero ni falso, ejecutará un código que nunca se ha llamado antes y que no se ha probado por completo, con resultados completamente impredecibles.

gnasher729
fuente
1

Estoy de acuerdo con la declaración de @ gnasher729: el enfoque de su profesor es totalmente equivocado.

Robusto significa que es resistente a roturas / fallas porque hace pocas suposiciones y está desacoplado: es autónomo, autodefinible y portátil. También incluye ser adaptable a los requisitos cambiantes. En una palabra, su código es duradero .

Esto generalmente se traduce en funciones cortas que obtienen sus datos de los parámetros pasados ​​por la persona que llama y el uso de interfaces públicas para los consumidores (métodos abstractos, envoltorios, indirección, interfaces de estilo COM, etc.) en lugar de funciones que contienen código de implementación concreto.

Vector
fuente
0

El código robusto es simplemente un código que maneja bien las fallas. Ni mas ni menos.

De las fallas, hay muchos tipos: código incorrecto, código incompleto, valores inesperados, estados inesperados, excepciones, agotamiento de recursos, ... El código robusto los maneja bien.

Chispeante
fuente
0

Consideraría el código que proporcionó como un ejemplo de programación defensiva (al menos mientras uso el término). Parte de la programación defensiva es tomar decisiones que minimicen los supuestos sobre el comportamiento del resto del sistema. Por ejemplo, cuál de estos es mejor:

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

O:

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

(En caso de que tenga problemas para ver la diferencia, verifique la prueba de bucle: los primeros usos !=, los segundos usos <).

Ahora, en la mayoría de las circunstancias, los dos bucles se comportarán exactamente de la misma manera. Sin embargo, el primero (en comparación con !=) supone que ise incrementará solo una vez por iteración. Si omite el valor sequence.length(), el ciclo podría continuar más allá de los límites de la secuencia y causar un error.

Por lo tanto, puede argumentar que la segunda implementación es más robusta: no depende de suposiciones acerca de si el cuerpo del bucle cambia i(nota: en realidad todavía asume que inunca es negativo).

Para motivar por qué es posible que no desee hacer esa suposición, imagine que el bucle está escaneando una cadena y procesando texto. Escribes el bucle y todo está bien. Ahora sus requisitos cambian y decide que necesita admitir caracteres de escape en la cadena de texto, por lo que cambia el cuerpo del bucle de modo que si detecta un carácter de escape (por ejemplo, barra invertida), se incrementa ipara omitir el carácter inmediatamente después del escape. Ahora el primer bucle tiene un error porque si el último carácter del texto es una barra diagonal inversa, el cuerpo del bucle se incrementará iy el bucle continuará más allá del final de la secuencia.

John Bartholomew
fuente
-1

Personalmente describo un código como 'robusto' que tiene este, atributos importantes:

  1. Si mi madre se sienta frente a él y trabaja con él, no puede romper el sistema.

Ahora, por descanso me refiero a poner el sistema en un estado inestable o causar una excepción NO MANEJADA . Ya sabes, a veces por un concepto simple, puedes hacer una definición y explicación compleja. Pero preferiría definiciones simples. Los usuarios son bastante buenos para encontrar aplicaciones robustas. Si el usuario de su aplicación le envía muchas solicitudes sobre errores, sobre pérdida de estado, sobre flujos de trabajo no intuitivos, etc., entonces hay algo mal con su programación.

Saeed Neamati
fuente