Pregunta

exención de responsabilidad: Soy una persona común el aprendizaje actualmente al programa. Nunca ha sido parte de un proyecto, ni nada escrito más de 500 líneas ~.

Mi pregunta es: ¿programación defensiva viola la no repita usted mismo principio? Suponiendo mi definición de la programación defensiva es correcta (que tiene la función de llamar a validar entrada en lugar de lo contrario), ¿No sería perjudicial para su código?

Por ejemplo, es tan malo:

int foo(int bar)
{
    if (bar != /*condition*/)
    {
        //code, assert, return, etc.
    }
}

int main()
{
    int input = 10;
    foo(input); //doesn't the extra logic
    foo(input); //and potentially extra calls
    foo(input); //work against you?
}   

comparado con esto:

int main()
{
    if (input == /*condition*/)
    {
        foo(input);
        foo(input);
        foo(input);
    }
}

Una vez más, como laico, no sé cuánto cuentan sentencias lógicas simples de vosotros como que respecta al rendimiento, pero sin duda la programación defensiva no es bueno para el programa o el alma.

¿Fue útil?

Solución

Todo se reduce a la contrato la interfaz proporciona. Hay dos escenarios diferentes para esto: entradas y salidas

.

Entradas - y con esto quiero básicamente media parámetros a funciones -. Deben ser controladas por la aplicación como regla general

Salidas - siendo los resultados de ida y vuelta -. Debe, básicamente, la confianza de la persona que llama, al menos en mi opinión

Todo esto se ve atenuada por esta pregunta: ¿qué ocurre si una de las partes rompe el contrato? Por ejemplo, digamos que usted tenía una interfaz:

class A {
  public:
    const char *get_stuff();
}

y que el contrato se especifica que no se devolverá una cadena nula (que va a ser una cadena vacía en el peor), entonces es seguro hacer esto:

A a = ...
char buf[1000];
strcpy(buf, a.get_stuff());

¿Por qué? Bueno, si se equivoca y el destinatario de la llamada devuelve un valor nulo, entonces el programa se bloqueará. Eso es realmente OK . Si algún objeto viola su contrato, entonces en general el resultado debe ser catastrófico.

El riesgo que enfrenta en ser demasiado defensivo es que se escribe un montón de código innecesario (que puede introducir más errores) o que en realidad podría enmascarar un problema serio por la ingestión de una excepción que realmente no debería.

Por circunstancias curso puede cambiar esto.

Otros consejos

La violación del principio DRY se parece a lo siguiente:

int foo(int bar)
{
    if (bar != /*condition*/)
    {
        //code, assert, return, etc.
    }
}

int main()
{
    int input = 10;
    if (input == /*condition*/)
    {
       foo(input);
       foo(input);
       foo(input);
    }
}

como se puede ver, el problema es que tenemos el mismo cheque dos veces en el programa, por lo que si los cambios de condiciones, hay que modificar la misma en dos lugares, y lo más probable es que nos olvidamos de uno de ellos, provocando un comportamiento extraño. DRY no significa "no ejecutar el mismo código dos veces", pero "no escriba el mismo código dos veces"

Permítanme primer estado que ciegamente siguiendo un principio es idealista y el mal. Es necesario para lograr lo que quiere lograr (por ejemplo, la seguridad de su aplicación), que suele ser mucho más importante que la violación de SECO. violaciónes intencional de principios son lo más a menudo necesaria en una buena programación.

Un ejemplo: hago controles dobles en las etapas importantes (por ejemplo LoginService - primero validar la entrada una vez antes de llamar LoginService.Login, y luego de nuevo en el interior), pero a veces tiendo a eliminar la exterior de nuevo más tarde después de que se aseguró de que todo funciona al 100%, por lo general el uso de pruebas de unidad. Depende.

Yo nunca había trabajado a lo largo de conseguir doble condición comprueba sin embargo. Olvidarlas por completo en otra parte suele ser peor múltiples magnitudes:)

Creo programación defensiva obtiene una especie de mala reputación, ya que hace algunas cosas que son una especie de indeseables, que incluyen código prolijo, y más significativamente, tapar errores.

La mayoría de las personas parecen estar de acuerdo que un programa falle rápido cuando se encuentra un error, pero que los sistemas de misión crítica de preferencia no deben fallar, y en lugar de hacer un gran esfuerzo para seguir adelante en la cara de los estados de error.

Hay un problema con esa afirmación, por supuesto, ¿Cómo puede un programa, incluso misión crítica, continúe cuando está en un estado incoherente. Por supuesto que no puede, en realidad.

Lo que queremos es que el programa de tomar todas las medidas razonables para hacer lo correcto, incluso si hay algo extraño pasando. Al mismo tiempo, el programa debería quejarse, ruidosamente , cada vez que se encuentra con un estado tan extraño. Y en el caso de que encuentre un error que no se puede recuperar, por lo general se debe evitar la emisión de una instrucción HLT, sino que debe fallar con gracia, el cierre de sus sistemas de seguridad o la activación de algún sistema de copia de seguridad, si está disponible.

En el ejemplo simplificado, sí, el segundo formato es probablemente preferible.

Sin embargo, que en realidad no se aplica a los programas más grandes y complejas, y más realistas.

Debido a que nunca se sabe de antemano dónde o cómo "foo" se utilizará, lo que necesita para proteger foo mediante la validación de la entrada. Si la entrada es validada por la persona que llama (por ejemplo. "Principal" en su ejemplo), entonces "principal" tiene que saber las reglas de validación, y aplicarlas.

En la programación del mundo real, las reglas de validación de entrada pueden ser bastante complejo. No es apropiado para hacer que la persona que llama saber todas las reglas de validación y aplicarlos correctamente. Algunos de llamada, en algún lugar, se va a olvidar las reglas de validación, o hacer las equivocadas. Así que es mejor poner la validación dentro "foo", incluso si se llamará repetidamente. Esto desplaza la carga de la persona que llama al abonado llamado, lo que libera la persona que llama a pensar menos en los detalles de "foo", y lo utilizan más como una interfaz abstracta y fiable.

Si realmente tiene un patrón donde "foo" se llamará varias veces con la misma entrada, que sugieren una función de contenedor que hace la validación de una vez, y una versión sin protección que elude la validación:

void RepeatFoo(int bar, int repeatCount)
{
   /* Validate bar */
   if (bar != /*condition*/)
   {
       //code, assert, return, etc.
   }

   for(int i=0; i<repeatCount; ++i)
   {
       UnprotectedFoo(bar);
   }
}

void UnprotectedFoo(int bar)
{
    /* Note: no validation */

    /* do something with bar */
}

void Foo(int bar)
{
   /* Validate bar */
   /* either do the work, or call UnprotectedFoo */
}

Al igual que Alex dijo, depende de la situación, por ejemplo, casi siempre validar la entrada en todas las etapas del proceso de conexión.

En otros lugares, que no necesitan todo eso.

Sin embargo, en el ejemplo que dio, estoy suponiendo, en el segundo ejemplo, que usted tiene más de una entrada, porque de lo contrario va a ser redundante llamando a la misma función 3 veces para la misma entrada que significa Tendrá que escribir la condición 3 veces. Ahora que es redundante.

Si la entrada siempre tiene que ser comprobado basta con incluir en la función.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top