Replace conditional with...



Conditional statements like "If" and "switch" multiply the number of possible flows the code execution can follows. So they increase the complexity and the number of tests required to verify the code.

=> Conditional statements are time and effort (costs €€€) multipliers






Here is a list of known techniques to eliminate conditionals :



  1. Replace conditional with polymorphism (look also the patterns template method and pluggable selector and  abstract factory)

  2. The patterns Null Object and Special Case

  3. The pattern State

  4. The pattern Pluggable Object

  5. The pattern Strategy

  6. The pattern Composite

  7. The pattern Visitor

  8. Replace conditional with a Dictionary (or with a conversion operator)

  9. Replace Conditional Dispatcher with Command

  10. Linearize the algorithm

Look also  "Replace nested conditionals with guards" to simplify the conditional before eliminating it.

some solutions like 6 and 10 completely eliminate the IF, some like 1 and 2 eliminate the duplication of the IF putting the IF logic in just one place, some like 8 reuse a IF logic tha is already implemented in the Framework so it prevent from creating a new IF.


Now I challenge you, show me an IF that cannot be eliminated !


Edit 5-Sep-2010:

  1. Replaced IF used to emulate break; or continue; in a nested FOR loop with a Return after extracting the nested IF in a new method. See Structured programming, Edsger Dijkstra (1969)
See also an example of code after removing IF and before full of IF here.

Look also: http://www.antiifcampaign.com/

Tags :   |  |  |  |

Print | posted @ Tuesday, April 20, 2010 11:58 PM

Comments on this entry:

Gravatar # re: Replace conditional with...
by Antonio Ganci at 4/21/2010 10:28 AM

> Now I challenge you, show me an IF that cannot be eliminated !

l'elenco che hai scritto serve per togliere gli if sul valore di variabili. Ci sono if che non si possono togliere perchè descrivono un comportamento mi spiego meglio:
if queue.IsEmpty
non lo puoi togliere.
Mentre:
If queue == null
if elementType == ...
ecc. si possono togliere.
Gravatar # re: Replace conditional with...
by Luca Del Tongo at 4/21/2010 11:52 AM

Ciao Antonio, se utilizzi automata based programming, penso proprio che tu possa eliminare anche gli if comportamentali in quanto il caso di pila vuota lo rappresenterai attraverso uno stato della fsm...
correggimi se sbaglio ;)
Gravatar # re: Replace conditional with...
by Antonio Ganci at 4/21/2010 12:05 PM

Volendo puoi fare così:
IF(queue.IsEmpty, true actions, false actions)

dove

void IF(bool condition, Action trueAction, Action falseAction)

o qualcosa di simile, così avresti un solo if nel calcolo della complessità ciclomatica, ma secondo me non andresti verso una migliore leggibilità del codice e avresti solo spostato l'if da un altra parte.
Non credo sia questo l'obiettivo del post di Luka ma quello di andare verso una minore complessità effettiva.
Gravatar # re: Replace conditional with...
by Luca Del Tongo at 4/21/2010 12:45 PM

@Antonio and Luka
Il caso di pila vuota, lo puoi affrontare in termini OO come suggerito da Luka, se invece ripeto usi uno stile basato sugli automi a stati finiti, invece che ritornare un null object ti porti in uno stato di pila vuota....
Proprio in un progetto personale che pubblicherò a breve (riguarda le regex e gli automi, ecco perchè adesso vedo tutto come una macchina a stati ;)) voglio effettuare refactoring applicando il pattern command invece che il dispatcher basato su if che sto utilizzando adesso per parsare una regex...
Ottimi consigli in questo post di Luka
Gravatar # re: Replace conditional with...
by Luca Minudel at 4/21/2010 12:57 PM

@Luca

quindi se ho capito bene usi il design pattern state e/o il design pattern command
Gravatar # re: Replace conditional with...
by Luca Del Tongo at 4/21/2010 1:16 PM

@Luka
Esatto, per la parte di parsing sicuramente il pattern command, e presumibilmente anche il pattern state al posto dell'implementazione attuale
Gravatar # re: Replace conditional with...
by Luca Del Tongo at 4/21/2010 3:04 PM

@Antonio
Dipende molto dal contesto in cui stai lavorando. Ad esempio ieri (se nn erro) ho letto il tuo post quello sulla malleabilità architetturale... mi trovo d'accordo con quanto hai scritto in quanto lo scenario che descrivi prevede dei possibili cambiamenti... molti però potrebbero trovare cmq complesso l'approccio descritto. Stessa cosa per la possibile soluzione che ho descritto che non ha valenza universale in quanto giustamente per alcuni può essere sovraingegnerizzata ma in alcuni contesti risulta la maggiormente adatta (ad esempio quando si modellano dei sistemi reattivi).
Gravatar # re: Replace conditional with...
by Luca Minudel at 4/21/2010 3:53 PM

@Antonio

quel "If sounds.Contains(keyCode)" andrebbe spostato dentro l'indexer sounds[keyCode] facendo in modo che restituisca il defaultSound per i tasti/keyCode non esplicitamente definiti

cosi facendo l'if lo incapsuli e qunidi non lo duplichi in giro per il codice.

o meglio ancora inizializzi la collezione sounds per tutti i keycode col suono di default e cosi non hai nemmeno un if.

riguardo il tuo ultimo quisito, personalmente preferisco eliminare l'if che scrivere meno righe di codice , questo post analizza bene l'argomento e mappa bene quanto ho visto nella pratica sull'argomento: http://blog.objectmentor.com/articles/2009/02/17/the-bloat-at-the-edge-of-duplication-removal-the-orange-model
Gravatar # re: Replace conditional with...
by LudovicoVan at 4/21/2010 3:59 PM

Personalmente sono daccordo: a volte il branching davvero denota una scarsa familiarita' con -per esempio- il poliformismo e persino le piu' elementari tecniche di programmazione strutturata; d'altra parte anche gli if/switch ecc. hanno un loro ruolo basilare nell'implementazione degli algoritmi in un linguaggio imperativo, e in quel caso fare a meno degli if adottando tecniche piu' di alto livello aggiunge solo complessita' non necessaria, mentre sicuramente toglie alla leggibilita'.

Fra parentesi, noterei che gli automi a stati finiti rappresentano un modello computazionale relativamente semplice, che non risolve le nostre esigenze di programmazione in generale: per esempio un parser vero e proprio si puo' fare con gli automi a stati finiti (o le sole regular expressions).

-LV
Gravatar # re: Replace conditional with...
by LudovicoVan at 4/21/2010 4:01 PM

> per esempio un parser vero e proprio si puo' fare con gli automi a stati finiti

Pardon, ovviamente intendo *non* si puo' fare...

-LV
Gravatar # re: Replace conditional with...
by LudovicoVan at 4/21/2010 4:09 PM

> quel "If sounds.Contains(keyCode)" andrebbe spostato dentro l'indexer sounds[keyCode] facendo in modo che restituisca il defaultSound per i tasti/keyCode non esplicitamente definiti

Ti risponderei che dipende: metti che e' un view-model, magari ha davvero senso restituire un default (ma comunque dipenderebbe dai requisiti), se invece fosse un model o una data class che ci sta sotto, restituirei il null e lascerei al client decidere se e come gestire la situazione, intercettare eccezioni, sollevare errori, e quant'altro.

Piu' in generale, mi sembra che il problema come al solito sia riuscire di volta in volta a capire dove sta quella soglia oltre la quale l'utile e dilettevole diventa inutile e addirittura dannoso.

My 2c, interessante dicussione.

-LV
Gravatar # re: Replace conditional with...
by Luca Del Tongo at 4/21/2010 4:10 PM

@Antonio and Luka
Personalmente sono pià vicino ad Antonio nel senso che in alcuni contesti preferisco tenere un if, non tanto per tenere meno righe di codice quanto piuttosto per rendere più human friendly la logica applicativa ad esempio dell'algoritmo che implemento...
il tutto IMVVVVHO

Gravatar # re: Replace conditional with...
by Luca Del Tongo at 4/21/2010 4:18 PM

@LudovicoVan
Ho scritto una cosa molto simile alla tua sull'utilizzo degli if negli algoritmi, quindi li ci troviamo d'accordo :)
Quando mi riferivo ad un parser nel mio caso dicevo un parser di regex...
Gravatar # re: Replace conditional with...
by Antonio Ganci at 4/21/2010 4:35 PM

@Antonio
quel "If sounds.Contains(keyCode)" andrebbe spostato dentro l'indexer sounds[keyCode] facendo in modo che restituisca il defaultSound per i tasti/keyCode non esplicitamente definiti

Si ok, sono d'accordo l'ho scritto perchè si capisse all'interno di un commento del blog.
Comunque mccabe rimarrebbe uguale ;-).

> o meglio ancora inizializzi la collezione sounds per tutti i keycode col suono di default e cosi non hai nemmeno un if.

Non sono d'accordo perchè non beccheresti le combinazioni con shift + tasto, ecc.
Gravatar # re: Replace conditional with...
by Luca Minudel at 4/24/2010 12:31 PM

@vic

in parte definire la funziona max é gia un modo di eliminare IF perché incapsula l'IF e quindi ne previene la duplicazione.

dall'altra, nel mondo reale semplicemente ri-usi Math.Max definito dal framework cosi non aggiungi neanche il primo IF.

infine guardando il codice che usa il max nella tua applicazione, magari scopri che l'algoritmo'e linearizzabile senza bisogno di max
Gravatar # re: Replace conditional with...
by Luca Minudel at 4/25/2010 3:37 PM

@mic

in questo caso torna utile il pattern State (preferibilmente implementato estendendo il PageHandlerFactory del aspx)

guarda il pattern, se hai domande specifiche legate ad aspx non esitare a chiedere



Gravatar # re: Replace conditional with...
by Luca Minudel at 4/25/2010 3:48 PM

@vic

se nella tua code-base quel if implementa quella logica "solo alle donne, con eta' minore di 21 anni, bionde, che..." compare _una_e_una_volta_sola_ in tutta la code-base sei giá apposto. non devi eliminarlo oltre.
se invece trovi la stessa logica condizionale in piú posti della code-base puoi usare le soluzioni 1,2,5,7 o 9 per eliminare le duplicazioni.


per evitare sin dall'inizio di creare le duplicazioni di quella logica condizionale, le stesse soluzioni 1,2,5,7 o 9 ti tornano utili.
Gravatar # re: Replace conditional with...
by miv at 4/25/2010 3:57 PM

piu' che altro vorrei capire se in un caso come questo vale la pena implementare altre classi/metodi per evitare 'una' condizione
Gravatar # re: Replace conditional with...
by Luca Minudel at 4/25/2010 8:50 PM

esatto. dallo sforzo nasce l'idea di come fare in efficace e conveniente. oppure domande che poi aiutano ad approfondire gli skil necessari ad evitare gli IF
Gravatar # re: Replace conditional with...
by vic at 4/25/2010 10:18 PM

ok, se cambiamo la questione originale:

Now I challenge you, show me an IF that cannot be eliminated !


in "gli if non si devono ripetere ma isolare"
(come peraltro ogni altro tipo di logica)

direi che siamo tutti d'accordo...

poi, sicuramente, alcuni if si possono realmente eliminare, ma non tutti; questi possono di norma essere isolati.
Gravatar # re: Replace conditional with...
by Luca Minudel at 4/25/2010 11:14 PM

concordo che questa frase é corretta.

quanti siano gli "alcuni if" che si possono eliminare é anche questo un punto importante su cui concordare.

in una code-base di un team é comune osservare che presi 10 if, tra quelli che sono duplicazione e quelli che sono del tutto eliminabili riisulta che 9 possono essere tolti.

per farlo serve conoscere i pattern relativi, riconoscere dove-quando applicarli e saperlo fare bene e in fretta in modo che sia sostenibile il tempo e sforzo. cioé la sfida é sugli skill da acquisire e allenare. quel IF che non puo essere eliminato non puo peró nemmeno essere usato per giustificare gli altri 9 che vengono lasciati li.

Gravatar # re: Replace conditional with...
by Tonino at 4/26/2010 3:26 PM

Interessante discussione.

Piccola esperienza:
Mi sono trovato a gestire una serie classi che applicavano regole di validazione, precedentemente gestite con degli "if", che venivano a volte rimesse in discussione, come requisiti, o in base al tipo di utente.
(un dato e' obbligatorio per qualcuno, non obbligatorio per qualcun'altro, e in un dato momento le cose cambiano)

Le regole di validazione erano codificata un po' come nel seguente modo:
if (string.IsNullOrEmpty(Nazionalita)) strErrore += ....


e di "if" ve ne erano un po' tanti, per ogni classe e per ogni tipo di dato coinvolto in quella classe.

Ho sostituito gli if (non tutti) con chiamate a un generico "constraintchecker" basato su dictionary nel quale vengono "iniettate" le logiche di validazione (con la tecnica dei delegates).

Quindi ora gli if sono sostituititi con la chiamata:
CheckConstraintForObject(this);

che a sua volta scandisce ed invoca i delegates, verificando prima se, da file di configurazione, e' stato definito che la regola deve essere attiva o meno.

Mi piaca abbastanza, anche nel senso del single responsability principle (la validazione, ed essa sola, e' affidata a...)

Non e' che ho ridotto di molto la complessita', pero' almeno e' stata separata e centralizzata una certa logica, ed e' stata consentita la possibilita' di decidere per diverse regole intervenendo in configurazione.

my 2 cents.

(p.s. sono stati comunque introdotti due if, che sono tra quelli che secondo me dovrebbero restare, un po' come nell'esempio fatto da Antonio. Se ne puo' parlare)

Ciao.

Your comment:

Title:
Name:
Email:
Website:
 
Italic Underline Blockquote Hyperlink
 
 
Please add 7 and 3 and type the answer here: