2010-09-08 23 views
18

Actualmente estoy tratando de reestructurar mi programa a ser más OO e implementar mejor los patrones conocidos etc.Cómo evitar múltiples FI anidados

tengo bastante muchos anidados sentencias if y quieren deshacerse de ellos. ¿Cómo puedo hacer esto? Mi primer enfoque fue hacerlo con excepciones, por lo que p.

public static Boolean MyMethod(String param) { 
if (param == null) 
    throw new NullReferenceException("param may not be null"); 

if (param.Equals("none") || param.Equals("0") || param.Equals("zero")) 
    throw new ArgumentNullException("param may not be zero"); 

// Do some stuff with param 
// This is not executed if param is null, as the program stops a soon 
// as one of the above exceptions is thrown 
} 

El método se utiliza en la clase principal de la aplicación, p.

static void Main() { 
try { 
    Boolean test = MyClass.MyMethod(null); // Will throw an exception 
} catch (Exception ex) { 
    MessageBox.Show(ex.Message, "Error"); 
} 

creo que esto es bastante agradable, ya que evita que las instrucciones anidadas y casi todos los métodos de las acciones están muy bien dispuestos en una sola planta.

Al igual que con las sentencias if, el método se vería así

public Boolean MyMethod(String param) { 
if (param != null) { 
    if (!param.Equals("none") && !param.Equals("0") && !param.Equals("zero")) { 
    // Do some stuff with param 
    } else { 
    MessageBox.Show("param may not be zero", "Error"); 
} else { 
    MessageBox.Show("param may not be null", "Error"); 
} 
} 

que me parece muy, muy feo y difícil de mantener.

Ahora, la pregunta es; ¿Este enfoque es bueno? Lo sé, eso podría ser subjetivo, pero ¿cómo superar las IF anidadas (1 o 2 niveles no son tan malos, pero empeora después de eso ...)

+3

Esto puede ser solo un código de muestra, pero realmente debería evitar lanzar Exception. Use ArgumentNullException, etc. –

+1

Y quizás lo más importante es que no debe tirar y luego detectar excepciones como esa. –

+0

Es un código de muestra, siempre trato de arrojar mejores excepciones nombradas para el caso. Y la captura, por supuesto, no ocurre en el mismo método, se hace en un nivel mucho más alto de la aplicación, más o menos justo antes de que lleguen al usuario. –

Respuesta

17

Realmente depende de su propósito. En su primera muestra, las declaraciones if cumplen el propósito de hacer cumplir un contrato, asegurándose de que la entrada al método cumpla con ciertos requisitos. En esos casos, mi propio código tiende a parecerse mucho a tu código.

En el caso de utilizar los bloques if para controlar el flujo de un método (en lugar de aplicar un contrato), a veces puede ser un poco más difícil. A veces me encuentro con un código como el (muy simplificado) siguiente ejemplo:

private void SomeMethod() 
{ 
    if (someCondition == true) 
    { 
     DoSomething(); 
     if (somethingElse == true) 
     { 
      DoSomethingMore(); 
     } 
    } 
    else 
    { 
     DoSomethingElse(); 
    } 
} 

En este caso, parece como si el método tiene varias responsabilidades, por lo que en este caso probablemente elegiría para dividirlo en varios métodos:

private void SomeMethod() 
{ 
    if (someCondition == true) 
    { 
     DoItThisWay(); 
    } 
    else 
    { 
     DoSomethingElse(); 
    } 
} 

private void DoItThisWay() 
{ 
    DoSomething(); 
    if (somethingElse == true) 
    { 
     DoSomethingMore(); 
    } 
} 

Esto hace que cada método mucho más simple con menos código anidado, y también puede aumentar la legibilidad, si se dan los métodos buenos nombres.

+0

Ver mi comentario - la captura no se realiza con el mismo método, sino que no tuve que escribir varios métodos; D –

4

Usted quiere investigar C#4 code contracts.

Un patrón de uso frecuente es DDD specification pattern para resúmenes de declaraciones if, aunque en su caso probablemente no sea adecuado.

+0

Eso es algo muy bueno en esos contratos. Los estaré investigando. –

+0

Patrón de especificación: ¿por qué no utilizar un parámetro de tipo Func ? –

0

Es realmente una pregunta bastante amplia de responder, ya que realmente dependería de la funcionalidad de las sentencias if.

Ciertamente, cuando sea posible, trato de reemplazar las sentencias if anidadas con un interruptor, pero esto no siempre es posible.

La comprobación de la validez de los parámetros es un buen enfoque, pero observe cómo alcanzarlo más arriba en el código, es decir, tener las declaraciones de tiro, pero no la captura en la clase que lo arroja.

+0

Ver la pregunta revisada. Era pereza, se maneja de manera diferente en el código real. –

17

Su problema es conocido como el anti-patrón de punta de flecha.

hay enfoques pragmáticos, como las declaraciones de la Guardia que has demostrado en la muestra, a los patrones de diseño enteros, evitando si (y más) todos juntos ...

Un montón de recursos sobre cómo resolverlos :

http://www.codinghorror.com/blog/2006/01/flattening-arrow-code.html

http://www.lostechies.com/blogs/chrismissal/archive/2009/05/27/anti-patterns-and-worst-practices-the-arrowhead-anti-pattern.aspx

http://elegantcode.com/2009/08/14/observations-on-the-if-statement/

1

Tal vez AspectF que podría ayudar en este caso:

public Boolean MyMethod(String param) { 
try { 
    AspectF.Define 
    .ErrorMsgIfNull(param, "must be not null") 
    .ErrorMsgIfEquals(new string[] {"None", "Zero", "0"}, "may not be zero") 
    //... 
    // use your own "AspectFlets" you wrote 
    //... 
    .Do(() => 
    { 
    // Do some stuff with param 
    // This is not executed if param is null, as the program stops a soon 
    // as one of the above exceptions is thrown 
    }); 
} 

Si tiene varias condiciones que cumplir (o evitar) que haría un bloque de feo de anidado si, de esta manera para factorizar código puede ayudar a hacer las cosas un poco más descriptivo.

Los métodos del código anterior son solo ejemplos que no existen, pero que se pueden implementar fácilmente.

Cuestiones relacionadas