Istruzioni switch di refactoring ed esiste un reale utilizzo delle dichiarazioni switch?

27

Stavo leggendo questo articolo e ci stavamo chiedendo, ci siamo sbarazzati di tutte le dichiarazioni switch sostituendole con un dizionario o una fabbrica in modo tale che non ci siano dichiarazioni di switch nei miei progetti.

Qualcosa non ha fatto abbastanza.

La domanda è: le istruzioni switch hanno un uso reale o le sostituiamo con un dizionario o un metodo factory (usando un metodo factory, ovviamente, ci sarà un uso minimo delle istruzioni switch per creando gli oggetti usando la fabbrica ... ma questo è tutto).

    
posta Kanini 04.05.2012 - 08:49
fonte

6 risposte

42

Entrambe le dichiarazioni di switch e il polimorfismo hanno il loro uso. Si noti tuttavia che esiste anche una terza opzione (nelle lingue che supportano i puntatori / lambda delle funzioni e le funzioni di ordine superiore): mappatura degli identificativi in questione alle funzioni del gestore. Questo è disponibile ad es. C che non è un linguaggio OO e C # che è *, ma non (ancora) in Java, anch'esso OO *.

In alcuni linguaggi procedurali (senza polimorfismo e funzioni di ordine superiore) le istruzioni switch / if-else erano l'unico modo per risolvere una classe di problemi. Così tanti sviluppatori, abituati a questo modo di pensare, hanno continuato a usare switch anche nei linguaggi OO, dove il polimorfismo è spesso una soluzione migliore. Questo è il motivo per cui spesso si consiglia di evitare / refactificare le dichiarazioni di switch a favore del polimorfismo.

In ogni caso, la soluzione migliore dipende sempre dal caso. La domanda è: quale opzione ti offre un codice più pulito, più conciso e più gestibile a lungo termine?

Le istruzioni switch possono spesso diventare ingombranti, avere dozzine di casi, rendere difficile la loro manutenzione. Dal momento che devi mantenerli in una singola funzione, quella funzione può diventare enorme. Se questo è il caso, dovresti prendere in considerazione il refactoring verso una soluzione basata su mappa e / o polimorfa.

Se lo stesso switch inizia a comparire in più punti, il polimorfismo è probabilmente l'opzione migliore per unificare tutti questi casi e semplificare il codice. Soprattutto se si prevede che verranno aggiunti altri casi in futuro; più posti è necessario aggiornare ogni volta, maggiori possibilità di errori. Tuttavia, spesso i singoli gestori di casi sono così semplici, o ce ne sono così tanti, o sono così interconnessi, che rifattorizzarli in una gerarchia di classe polimorfica completa è eccessivo, o si traduce in un sacco di codice duplicato e / o ingarbugliato, difficile da mantenere la gerarchia di classi. In questo caso, potrebbe essere più semplice utilizzare invece le funzioni / lambdas (se la lingua lo consente).

Tuttavia, se disponi di una switch in un singolo luogo, con pochi casi che fanno qualcosa di semplice, potrebbe essere la soluzione migliore per lasciarla come è.

* Uso liberamente il termine "OO" qui; Non sono interessato ai dibattiti concettuali su ciò che è "reale" o "puro" OO.

    
risposta data 04.05.2012 - 10:31
fonte
14

È qui che torno ad essere un dinosauro ...

Le istruzioni switch non sono male per se stesse, è l'uso che ne fa di quelle in questione.

La più ovvia è la "stessa" istruzione ripetuta più e più volte attraverso il tuo codice che è male (stato lì, fatto, farebbe ogni sforzo per non farlo di nuovo) - e in quest'ultimo caso potresti essere in grado di affrontare l'uso del polimorfismo. Di solito c'è anche qualcosa di abbastanza orribile nei casi nidificati (ero solito avere un mostro assoluto - non del tutto sicuro di come lo affrontassi ora diverso da "migliore").

Il dizionario come interruttore trovo più difficile - fondamentalmente sì, se il tuo switch copre il 100% dei casi, ma dove vuoi avere un default o nessun caso di azione inizia a diventare un po 'più interessante.

Penso che sia una questione di evitare la ripetizione e di assicurarsi che stiamo componendo i nostri grafici oggetto nei posti giusti.

Ma c'è anche l'argomento della comprensione (manutenibilità) e questo taglia in entrambi i modi - una volta compreso come tutto funziona (il modello e l'app in cui è implementato) è facile ... ma se si arriva a una singola riga di codice in cui devi aggiungere qualcosa di nuovo, quindi devi saltare dappertutto per capire cosa è necessario aggiungere / modificare.

Per quanto riguarda la capacità massiccia dei nostri ambienti di sviluppo, sento ancora che è desiderabile essere in grado di capire il codice (come se fosse) stampato su carta - puoi seguire il codice con il dito? Accetto che in realtà no, con molte buone pratiche oggi non puoi e per buoni motivi, ma questo significa che è più difficile iniziare a lavorare con il codice (o forse sono solo vecchio ...)

    
risposta data 04.05.2012 - 10:53
fonte
8

Le dichiarazioni switch e il polimorfismo dei sottotipi sono un vecchio problema e vengono spesso citati nella comunità FP nelle discussioni sul Problema di espressione .

Fondamentalmente, abbiamo tipi (classi) e funzioni (metodi). Come possiamo codificare le cose in modo che sia facile aggiungere nuovi tipi o nuovi metodi su?

Se si programma in stile OO, è difficile aggiungere un nuovo metodo (poiché ciò significherebbe il refactoring di tutte le classi esistenti), ma è molto semplice aggiungere nuove classi che usano gli stessi metodi di prima.

D'altra parte, se usi un'istruzione switch (o il suo equivalente OO, il pattern Observer) allora è molto facile aggiungere nuove funzioni ma è difficile aggiungere nuovi casi / classi.

Non è facile avere una buona estensibilità in entrambe le direzioni, quindi quando scrivi il tuo codice, determina se usare il polimorfismo o le istruzioni di commutazione in base alla direzione in cui è più probabile che si estenda.

    
risposta data 04.05.2012 - 22:14
fonte
4
do we get rid of all switch statements by replacing them with a Dictionary or a Factory so that there are no switch statements at all in my projects.

No. Tali assoluti sono raramente una buona idea.

Molte posizioni, un dispiegamento dizionario / ricerca / fabbrica / polimorfica forniranno un design migliore rispetto a un'istruzione switch, ma dovrai comunque popolare il dizionario. In alcuni casi, ciò oscurerà ciò che sta effettivamente accadendo e semplicemente avere l'istruzione switch in linea è più leggibile e mantenibile.

    
risposta data 04.05.2012 - 20:54
fonte
2

Visto che questo è indipendente dalla lingua, il codice fall-through funziona meglio con le dichiarazioni di switch :

switch(something) {
   case 1:
      foo();
   case 2:
      bar();
      baz();
      break;

   case 3:
      bang();
   default:
      bizzap();
      break;
}

Equivalente a if , con un caso predefinito scomodo molto . E tieni presente che maggiore è il numero di fall-through, più lunga sarà la lista delle condizioni:

if (1 == something) {
   foo();
}
if (1 == something || 2 == something) {
   bar();
   baz();
}
if (3 == something) {
   bang();
}
if (1 != something && 2 != something) {
   bizzap();
}

(Tuttavia, dato quello che dicono alcune delle altre risposte, mi sento come se avessi perso il punto della domanda ...)

    
risposta data 04.05.2012 - 20:17
fonte
1

Le istruzioni switch come differenziazione del caso hanno un uso reale. I linguaggi di programmazione funzionale utilizzano qualcosa chiamato corrispondenza del modello che consente di definire le funzioni in modo diverso a seconda dell'input. Tuttavia, nelle lingue orientate agli oggetti lo stesso obiettivo può essere raggiunto in modo più elegante utilizzando il polimorfismo. Basta chiamare il metodo e, in base al tipo effettivo dell'oggetto, verrà eseguita l'implementazione corrispondente del metodo. Questa è la ragione dietro l'idea, che le dichiarazioni di commutazione sono un odore di codice. Tuttavia, anche nelle lingue OO, potresti trovarle utili per implementare il modello astratto di fabbrica.

tl; dr - sono utili, ma abbastanza spesso puoi fare di meglio.

    
risposta data 04.05.2012 - 09:01
fonte

Leggi altre domande sui tag