2010-09-08 11 views
18

Attualmente sto cercando di ristrutturare il mio programma per essere più OO e di attuare meglio i modelli noti eccCome evitare multipli FI nidificati

ho abbastanza molti nidificati IF-dichiarazioni e vogliono sbarazzarsi di loro. Come posso andare su questo? Il mio primo approccio era quello di farlo con le eccezioni, quindi ad es.

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 
} 

Il metodo è utilizzato nella classe principale dell'applicazione, ad es.

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

Credo che questo è molto bello, in quanto impedisce le istruzioni nidificate e quasi tutti i metodi azioni sono ben disposti su un livello.

Come con IF-dichiarazioni, il metodo sarebbe simile a questa

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"); 
} 
} 

che trovo molto, molto brutto e difficile da mantenere.

Ora, la domanda è; è questo approccio buono? Lo so, potrebbe essere soggettivo, ma come superare gli IF annidati (1 o 2 livelli non sono poi così male, ma dopo questo peggiora ...)

+3

Questo potrebbe essere solo un codice di esempio, ma si dovrebbe davvero evitare di lanciare l'eccezione. Usa ArgumentNullException ecc. –

+1

E forse ancora più importante non devi gettare e quindi prendere eccezioni come quella. –

+0

È un codice di esempio, cerco sempre di lanciare eccezioni con il nome migliore per il caso. E il catching, naturalmente, non si presenta nello stesso modo, è fatto su un livello molto più alto dell'applicazione, più o meno appena prima che si gonfiano fino all'utente. –

risposta

17

Dipende davvero dal loro scopo. Nel primo campione, le istruzioni if ​​servono allo scopo di far rispettare un contratto, assicurandosi che l'input del metodo soddisfi determinati requisiti. In questi casi, il mio codice tende ad assomigliare molto al tuo codice.

Nel caso di utilizzare i blocchi if per controllare il flusso di un metodo (piuttosto che imporre un contratto), a volte può essere un po 'più difficile. A volte mi imbatto in codice come il seguente (estremamente semplificato) Esempio:

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

In questo caso, sembra come se il metodo ha diverse responsabilità, quindi in questo caso avrei probabilmente scegliere di dividerlo in diversi metodi:

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

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

Questo rende ogni metodo molto più semplice con il codice di meno nidificato, e può anche aumentare la leggibilità, se i metodi sono dati nomi buoni.

+0

Vedere il mio commento: la cattura non viene eseguita con lo stesso metodo, era solo così non ho dovuto scrivere diversi metodi; D –

4

Il modo in cui si desidera esaminare C#4 code contracts.

Un motivo spesso utilizzato è DDD specification pattern per l'estrazione di istruzioni, anche se nel tuo caso probabilmente non è adatto.

+0

Questa è una cosa molto carina quei contratti. Li guarderò. –

+0

Schema delle specifiche: perché non utilizzare un parametro di tipo Func ? –

0

È una domanda piuttosto ampia a cui rispondere in quanto dipenderebbe davvero dalla funzionalità delle istruzioni if.

Certamente, dove possibile, provo a sostituire nidificando se le istruzioni con uno switch, ma questo non è sempre possibile.

Il controllo della validità dei parametri è un buon approccio, ma osservate la cattura più in alto nel codice, cioè le istruzioni throw, ma non il catch nella classe che lo lancia.

+0

Vedere la domanda rivista. Era la pigrizia, è gestita in modo diverso nel codice reale. –

17

Il tuo problema è noto come la freccia anti-modello.

ci sono approcci pragmatici, come ad esempio le dichiarazioni della Guardia hai mostrato nel campione, a modelli di progettazione integrali, evitando se (e altro) tutti insieme ...

sacco di risorse su come risolverli :

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

Forse AspectF potrebbe aiutare in questo 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 
    }); 
} 

Se si dispone di più le condizioni per soddisfare (o da evitare) che avrebbe fatto un brutto blocco di nidificato se, in questo modo per fattorizzare il codice può aiutare a fare le cose un po 'più descrittivo.

I metodi nel codice sopra riportato sono solo esempi che non esistono, ma che possono essere facilmente implementati.