Newsgroup: it.comp.lang.c++
Titolo: eliminare switch..case

ciao,
i vecchi programmi C spesso abbondavano di switch...case, struttura direi
poco manutenibile.
Ad es
switch (valA) {
case A1: esegui1(); break;
case A2 esegui2(); break;
case A3:esegui3(); break;
default:........
}
dove A e' un tipo per il quale abbia senso (credo) il confronto valA == A1.
e esegui() fa qualcosa, magari con dati della classe in cui e' immerso lo
switch...case.
Avete qualche idea, che magari si avvicina a qualche pattern, per "aggirare"
cio',
col solito occhio verso la riusabilita' e manutenibilita'?
grazie
tiberio

 wrote in message
38f77...@news.dada.it...

Quando lo switch si avvicina a un "typeswitch", puo` essere spesso
sostituito da una chiamata ad un metodo virtuale; in altri casi (e
anche in C), conviene un array di puntatori a funzione.
Ma di solito occorre rifattorizzare pesantemente per raggiungere
questi miglioramenti.
Alex

col solito occhio verso la riusabilita' e manutenibilita'?
Bisognerebbe conoscere il problema, spesso il polimorfismo
riduce di molto l'uso di costrutti condizionali.
Paolo
--------------------------------------------------
  Paolo Marotta
  Computer Science, University of Pisa
  home:   
http://www.cli.di.unipi.it/~marotta
--------------------------------------------------

col solito occhio verso la riusabilita' e manutenibilita'?
polimorfismo.

col risultato che uno salta e si perde una giornata in debug.
Probabilmente non e' corretto l'approccio tramite switch...case,
probabilmente in questo caso e' piu' corretto costruire una classe
"TSchedaVideo" le cui classi derivate hanno comportamento polimorfico
(e quindi distinto l'una dall'altra). Da questo a dire che
switch...case e' in assoluto poco mantenibile ce ne passa.
Uno dei vantaggi del polimorfismo e' proprio la capacita' di rendere
il vecchio codice adattabile al nuovo codice, senza modificazioni.
Esempio.:
void TracciaLinea( int x0, int y0, int x1, int y1, TipoScheda S )
{
     switch ( S )
     {
            case CGA : CGALine(x0,y0,x1,y1) ; break ;
            case EGA : EGALine(x0,y0,x1,y1) ; break ;
            case VGA : VGALine(x0,y0,x1,y1) ; break ;
     }       
}
main()
{
    TipoScheda SchedaInstallata = VGA ;    
 
    TracciaLinea( 10, 10, 100, 100, SchedaInstallata ) ;
}
In questo caso e' chiaro che volendo supportare la nuova scheda
"SuperVideoCard", bisogna intaccare il vecchio codice.
Che ne dici invece di :
class TSchedaVideo
{
      public:
      virtual void TracciaLinea( int x0, int y0, int x1, int y1 ) ;
} ;
class TSchedaVideoVGA : public TSchedaVideo
{
      void TracciaLinea( int x0, int y0, int x1, int y1 ) ;
}
void TSchedaVideoVGA::TracciaLinea( int x0, int y0, int x1, int y1 )
{
     VGALine( x0, y0, x1, y1 ) ;
}
class TSchedaVideoCGA : public TSchedaVideo
{
      void TracciaLinea( int x0, int y0, int x1, int y1 ) ;
}
void TSchedaVideoCGA::TracciaLinea( int x0, int y0, int x1, int y1 )
{
     CGALine( x0, y0, x1, y1 ) ;
}
class TSchedaVideoEGA : public TSchedaVideo
{
      void TracciaLinea( int x0, int y0, int x1, int y1 ) ;
}
void TSchedaVideoEGA::TracciaLinea( int x0, int y0, int x1, int y1 )
{
     VGALine( x0, y0, x1, y1 ) ;
}
void TracciaLinea(TSchedaVideo VCard, int x0, int y0, int x1, int y1)
{
     VCard.TracciaLinea( x0, y0, x1, y1 ) ;
}
In un secondo momento mi bastera' aggiungere :
class TSchedaVideoSuperVideoCard : public TSchedaVideo
{
      void TracciaLinea( int x0, int y0, int x1, int y1 ) ;
}
void TSchedaVideoSuperVideoCard::TracciaLinea( 
     int x0, int y0, int x1, int y1 )
{
     SuperVideoCardLine( x0, y0, x1, y1 ) ;
}
Ed il vecchio codice funzionera' senza modifiche.

credo regga poco, almeno dal punto di vista dell'efficienza.
Che discorsi sono ?
Un progetto in cui ci sono 1000 entita' uguali e' ingestibile
comunque, anche se, prese singolarmente, queste entita' sono
facilmente mantenibili.
Ma a questo punto puo' essere sbagliato il modo di progettare.
Una cosa, poi, e' se si discute l'efficienza, ben altra cosa e' la
mantenibilita'.
switch-case regge benissimo nel caso ce ne siano 2, come nel caso ce
ne siano 1000. In fondo si salta solo di confronti e salti.

Perche` poco manutenibile
                                               ^^^^^
Sorry, ho dimenticato un punto interrogativo!


cosa, per es: il tipo di scheda
Chi ha scritto codice simile ha utilizzato il C++ come un normale 
linguaggio procedurale. L'errore sta nell'avere utilizzato lo
switch per gestire azioni legate al tipo di certi oggetti.
In C++ un tale codice e` spesso indice di cattivo design,
quelle situazioni solitamente si risolvono col polimorfismo.
Non e` lo switch che e` poco manutenibile, ma il codice nel suo
complesso che e` stato mal realizzato.


questo thread poteva generare indicazioni generiche su cui poi lavorare.
Lo switch e` utilissimo quando devi implementare un "salto a molte vie",
naturalmente come molti altri costrutti, puo` essere utilizzato 
(erroneamente) per fare cose per le quali altri meccanismi sono piu`
adatti.
Ti sara` sicuramente capitato di vedere cose tipo
if (i =1)
else if (i=5)
         else if(i = 7)
                  else if(i=2)
Quello e` un tipico esempio di switch mancato!
Il caso delle schede video era invece un esempio di
polimorfismo mancato.


 credo regga poco, almeno dal punto di vista dell'efficienza.
Non si puo` mai dire -- un buon compilatore puo` riuscire a
generare una "jump table", e l'efficienza va a posto.
Il problema e` "come garantire che tutti i casi siano coperti"
se ne vengono generati dei nuovi nella revisione del programma;
un problema di correttezza, non di efficienza.
Se si imposta tutto il possibile in termini di chiamate a
metodi virtuali di classi astratte (ottenute per reference),
la problematica svanisce.  Ma questa e` una ristrutturazione
molto profonda ed "invasiva" se il codice esistente e` tutto
pensato in termini di switch.
Alex


Tiberio


Hai ragione, ogni caso puo' avere la sua soluzione, ma magari
questo thread poteva generare indicazioni generiche su cui poi lavorare.
grazie ancora,
tiberio

 complesso che e` stato mal realizzato.
Il costrutto switch...case potrebbe presentare problemi di mantenibilità
qualora venisse utilizzato, con le stesse opzioni, in varie zone del codice.
Il caso tipico è rappresentato dalla necessità di modificare tutti gli
switch...case nel caso dovesse essere introdotto un nuovo case (con gli ovvi
rischi di introduzione di errori - vedi copia/incolla).
In tali situazioni l'utilizzo del pattern State permette di eliminare lo
switch...case rendendo dinamico lo stato dell'oggetto in modo da rispondere
trasparentemente al cambiamento di condizioni rappresentato dai casi
presenti nello switch.
MM

Ho dimenticato l'utilizzo del pattern Strategy in sostituzione di
switch...case.
Per una descrizione/motivazione rimando a
http://ootips.org/strategy-vs-case.html
MM

rischi di introduzione di errori - vedi copia/incolla).
In ogni caso mi pare comunque si concordi sul fatto che il problema non
e' lo switch ma il modo in cui viene utilizzato. Anche nel caso di
switch ripetuto piu' volte... 
Ritengo che normalmente tali situazioni, 
piu' che dimostrare le mancane di un costrutto, dimostrino un suo
errato uso, d'altronde se vogliamo ogni costrutto e' potenzialmente
poco manutenibile, un while, un if ripetuti in piu' punti.
Se fosse semplice dire questo va bene, quell'altro no avremmo gia'
da un po' il Linguaggio anziche' piu' linguaggi (che spesso oltretutto
seguono scelte molto simili).
Paolo

 errato uso...
[snip]
La dimostrazione di quanto dici è data dal fatto che spesso il costrutto
switch...case viene sostituito da soluzioni più eleganti (almeno a mio
avviso) e più funzionali proprio durante una fase di ristrutturazione del
codice (refactoring) che avviene quando, parafrasando Kent Beck, lo stesso
codice 'puzza'. E nel caso dello stesso switch...case presente in diverse
parti del codice si sente proprio una forte 'puzza'.
MM

 e' lo switch ma il modo in cui viene utilizzato.
ciao a tutti,
io penso che Massimo si riferisse alla possibile eliminazione di switch-case
uguali e presenti in numero > 1 nel codice, indipendentemente dall'uso.
E' vero che in questi casi i taglia-incolla selvaggi sono pericolosi.
Ora, per quanto mi riguarda, leggero' quanto consigliato.
ciao e grazie ancora
tiberio