Domanda

Esiste un modo alternativo per implementare un caso switch in Java diverso da if else che non sembra buono. Un insieme di valori sarà lì in combinazione, secondo la selezione deve essere eseguito il metodo corrispondente.

È stato utile?

Soluzione

Presumibilmente stai lottando con la necessità che il caso sia costante. In genere si tratta di un odore di codice, ma ci sono cose che puoi fare. Potresti voler sollevare e collegare ad un'altra domanda che spiega in dettaglio perché stai cercando di cambiare.

Map<String,Object> map = new HasMap<String,Object>();
// ... insert stuff into map
// eg: map.add("something", new MyObject());

String key = "something";
if (map.contains(key)) {
    Object o = map.get(key);
}

Nell'esempio sopra, potresti voler mappare a "gestori", qualcosa come

interface Handler {
    public void doSomething();
}

che poi trasforma tutto questo in una ricerca.

if (map.contains(key)) { map.get(key).doSomething(); }

Ancora una volta, è un po 'di odore, quindi per favore pubblica una domanda che illustri il ragionamento.

Altri suggerimenti

Se hai molte dichiarazioni switch / case attorno al tuo codice e ti stanno facendo impazzire.

Puoi optare per il refactoring: Sostituisci condizionale con polimorfismo.

Supponiamo che tu abbia un software utilizzato per salvare informazioni su diversi dispositivi: sono definite 4 operazioni di persistenza: recupera, salva, cancella, aggiorna , che potrebbe essere implementato da N numero di meccanismi di persistenza (file flat, rete, RDBMS, XML, ecc.).

Il tuo codice deve supportarli tutti, quindi in 4 posti diversi hai questo:

prima

class YourProblematicClass { 

....

     public void fetchData( Object criteria ) {

          switch ( this.persitanceType ) {
              case FilePersistance:
                  // open file
                  // read it
                  // find the criteria
                  // build the data
                  // close it.
                  break;
               case NetWorkPersistance: 
                   // Connect to the server
                   // Authenticate
                   // retrieve the data 
                   // build the data 
                   // close connection
                   break(); 
                case DataBasePersistace:
                   // Get a jdbc connection
                   // create the query
                   // execute the query
                   // fetch and build data
                   // close connection
                   break;
           }
           return data;
         }    

Lo stesso per salvare / cancellare / aggiornare

 public void saveData( Object data) {

      switch ( this.persitanceType ) {
          case FilePersistance:
              // open file, go to EOF, write etc.
              break;
           case NetWorkPersistance: 
               // Connect to the server
               // Authenticate
               // etc
               break(); 
            case DataBasePersistace:
               // Get a jdbc connection, query, execute...
               break;
       }
     }

E così via ....

 public void deleteData( Object data) {

      switch ( this.persitanceType ) {
          case FilePersistance:
              break;
           case NetWorkPersistance: 
               break(); 
            case DataBasePersistace:
               break;
       }
  }

 public  void updateData( Object data) {

      switch ( this.persitanceType ) {
          case FilePersistance:
              break;
           case NetWorkPersistance: 
               break(); 
            case DataBasePersistace:
               break;
       }
  }

L'uso dell'istruzione switch / case diventa problematico:

  • Ogni volta che si desidera aggiungere un nuovo tipo, è necessario inserire un nuovo interruttore / caso in ogni sezione.

  • Molte volte, alcuni tipi sono simili e non hanno bisogno di un interruttore / caso diverso (potresti metterli in cascata)

  • Alcuni altri lo sono e alcune volte differiscono leggermente
  • Potrebbe anche essere necessario caricare tipi diversi in fase di esecuzione (come plug-in)

Quindi il refactoring qui sarebbe aggiungere un'interfaccia o un tipo astratto e avere i diversi tipi implementare quell'interfaccia e delegare la responsabilità a quell'oggetto.

Quindi avresti qualcosa del genere:

DOPO

   public interface PersistenceManager {
        public void fetchData( Object criteria );
        public void saveData( Object toSave );
        public void deleteData( Object toDelete );
        public void updateData( Object toUpdate );
   }  

E diverse implementazioni

  public class FilePersistence implements PersistanceManager {
        public void fetchData( Object criteria ) {
                  // open file
                  // read it
                  // find the criteria
                  // build the data
                  // close it.
         }
        public void saveData( Object toSave ) {
                 // open file, go to EOF etc. 
        }
        public void deleteData( Object toDelete ){
           ....
        }
        public void updateData( Object toUpdate ){
          ....
        }
  }

E gli altri tipi si implementerebbero secondo la loro logica. La rete avrebbe a che fare con socket e stream, DB avrebbe a che fare con JDBC, ResultSet ecc. XML con nodo etc.etc.

  public class NetworkPersistence implements PersistanceManager {
        public void fetchData( Object criteria ) {
             // Socket stuff
         }
        public void saveData( Object toSave ) {
             // Socket stuff
        }
        public void deleteData( Object toDelete ){
           // Socket stuff
        }
        public void updateData( Object toUpdate ){
           // Socket stuff
        }
  }


  public class DataBasePersistence implements PersistanceManager {
        public void fetchData( Object criteria ) {
             // JDBC stuff
         }
        public void saveData( Object toSave ) {
             // JDBC  stuff
        }
        public void deleteData( Object toDelete ){
           // JDBC  stuff
        }
        public void updateData( Object toUpdate ){
           // JDBC  stuff
        }
  }

E infine devi solo delegare le invocazioni.

In seguito:

public YouProblematicClass { // not longer that problematic

    PersistamceManager persistance = // initialize with the right one.


        public void fetchData( Object criteria ) {
             // remove the switch and replace it with:
             this.persistance.fetchData( criteria );
         }
        public void saveData( Object toSave ) {
             // switch removed
             this.persistance.saveData(  toSave );
        }
        public void deleteData( Object toDelete ){
           this.persistance.deleteData( toDelete );
        }
        public void updateData( Object toUpdate ){
           this.persistance.updateData( toUpdate );
        }
  }

Quindi, devi solo creare l'istanza corretta per il gestore di persistenza in base al tipo una sola volta. Quindi tutte le invocazioni sono risolte dal polimorfismo. Questa è una delle caratteristiche chiave della tecnologia orientata agli oggetti.

Se decidi di aver bisogno di un altro gestore di persistenza, devi solo creare la nuova implementazione e assegnarla alla classe.

 public WavePersistance implements PersistanceManager {

        public void fetchData( Object criteria ) {
             // ....
         }
        public void saveData( Object toSave ) {
             //  ....
        }
        public void deleteData( Object toDelete ){
           //  ....
        }
        public void updateData( Object toUpdate ){
           //  ....
        }
   }

Il refactoring del codice per utilizzare il polimorfismo potrebbe eliminare la necessità di un'istruzione switch. Tuttavia, ci sono alcuni usi legittimi per passare quindi dipende dalla tua situazione.

una brutta serie di if, else if, else ?

oppure si potrebbe immaginare una specie di caso di commutazione dinamica:

public interface Task<T>
     {
     public void doSomething(T context);
     }

public Class SwitchCase<T>
     {
     Map<Integer,Task<T>> tasks;
     Task<T> defaultTask;

     public void choose(int choice, T context)
         {
         Task<T> t= this.tasks.get(choice);
         if(t!=null) { t.doSomething(context); return;}
         if(defaultTask!=null)  { defaultTask.doSomething(context);}
         }
     }

Immagino " Clean Code " ha un bel capitolo secondo switch / case vs. if / else.

Inoltre: penso che abbia senso decidere se è possibile ridurre il "rumore". e rendere il codice più pulito usando la custodia, il polimorfismo o anche un buon vecchio if / else. Il numero di casi svolge un ruolo importante qui, immagino.

Pubblico un caso tipico di come ho sostituito switch case con enum.

prima del refactor ho enum PatternTypes :

public enum PatternTypes {

    ALPHA_CHAR, ALPHANUMERIC_CHAR, ADDITIONAL_CHAR, UNICODE_BMP_CHARS
}

e funzione:

private static final String ALPHA_CHAR = "[a-zA-Z]+";
    private static final String ALPHANUMERIC_CHAR = "[a-zA-Z0-9\\_]+";
    private static final String ADDITIONAL_CHAR = "[a-zA-Z0-9\\_\\-\\,\\.\\s\\!\\#\\$\\&\\(\\)\\*\\+\\;\\:\\=\\?\\@\\|\\[\\]\\{\\}\\~]+";
    private static final String UNICODE_BMP_CHARS = "[a-zA-Z0-9\\_\\-\\,\\.\\s\\!\\#\\$\\&\\(\\)\\*\\+\\;\\:\\=\\?\\@\\|\\[\\]\\{\\}\\~\u00A0-\uD7FF\uF900-\uFFFD]+";

/*
     * Match given classAbbr with given RegEx pattern
     */
    private void checkInvalidClassAbbr(String classAbbr,
            PatternTypes classAbbrPattern) {

        switch (classAbbrPattern) {
        case ALPHA_CHAR:

            checkUnmatched(classAbbr, ALPHA_CHAR, CLASS_ABBR_VAR_NAME);
            break;
        case ALPHANUMERIC_CHAR:

            checkUnmatched(classAbbr, ALPHANUMERIC_CHAR, CLASS_ABBR_VAR_NAME);
            break;
        case ADDITIONAL_CHAR:
            throw new MalFormedDNException("Not support Pattern Type:"
                    + classAbbrPattern);

        case UNICODE_BMP_CHARS:
            throw new MalFormedDNException("Not support Pattern Type:"
                    + classAbbrPattern);
        }

    }

Dopo refactor PatternTypes modificato in:

public enum PatternTypes {

    /**
     * RegEx patterns divided by restriction level
     */
    ALPHA_CHAR("[a-zA-Z]+"),
    ALPHANUMERIC_CHAR("[a-zA-Z0-9\\_]+"),
    ADDITIONAL_CHAR("[a-zA-Z0-9\\_\\-\\,\\.\\s\\!\\#\\$\\&\\(\\)\\*\\+\\;\\:\\=\\?\\@\\|\\[\\]\\{\\}\\~]+"),
    UNICODE_BMP_CHARS("[a-zA-Z0-9\\_\\-\\,\\.\\s\\!\\#\\$\\&\\(\\)\\*\\+\\;\\:\\=\\?\\@\\|\\[\\]\\{\\}\\~\u00A0-\uD7FF\uF900-\uFFFD]+");

    public String getPatternContent() {
        return patternContent;
    }

    private String patternContent;

    PatternTypes(String patternContent) {
        this.patternContent = patternContent;
    }
}

e la funzione si semplifica in:

/*
     * Match given classAbbr with given RegEx pattern
     */
    private void checkInvalidClassAbbr(String classAbbr, PatternTypes classAbbrPattern) {

            if (PatternTypes.ADDITIONAL_CHAR.equals(classAbbrPattern) || PatternTypes.UNICODE_BMP_CHARS.equals(classAbbrPattern)){
                throw new MalFormedDNException("RegEx pattern:" + classAbbrPattern.name() + "is not allowed for Class Abbr");
            }

            checkUnmatched(classAbbr, classAbbrPattern.getPatternContent(), CLASS_ABBR_VAR_NAME);

    }

Hashmap è considerato non compatibile con la memoria, quindi è possibile utilizzare Enum per questo scopo.

Esempio:

class EnumExample4{
enum Season{ 
WINTER(5), SPRING(10), SUMMER(15), FALL(20); 

private int value;
private Season(int value){
this.value=value;
}
}
public static void main(String args[]){

System.out.println(Season.WINTER.value); //This gives you 5
}}

Questo ti consentirà di scrivere Switch Case o if statement.

Per un'alternativa allo switch switch, penso che la soluzione migliore sarà utilizzare un enum . Ad esempio: considera il caso seguente: -

    public enum EnumExample {

  OPTION1{

    public double execute() {
      Log.info(CLASS_NAME, "execute", "The is the first option.");
      return void;
    }

  },
  OPTION2{

    public double execute() {
      Log.info(CLASS_NAME, "execute", "The is the second option.");
      return void;
    }

  },
  OPTION3{

    public double execute() {
      Log.info(CLASS_NAME, "execute", "The is the third option.");
      return void;

  };

  public static final String CLASS_NAME = Indicator.class.getName();

  public abstract void execute();

}

L'enumerazione di cui sopra può essere utilizzata nel modo seguente:

EnumExample.OPTION1.execute();

Speriamo che questo vi aiuti ragazzi.

Cosa vuoi fare? Perché Switch-Case non è abbastanza buono?

La risposta veloce è: usa if-else

    if () {}
    else if () {}
...
    else if () {}

Ma non direi che è meglio ...

Che ne dici di un'istruzione if (insieme a else if e else )? Mentre switch ti consentirà di passare utilizzando l'uguaglianza rispetto ai tipi interi o Enum, se ti consente di utilizzare qualsiasi logica booleana.

Potresti sempre sostituire un interruttore con if-else if-else if-else if ... , anche se non vedo perché lo vorresti. A seconda del contesto, switch può talvolta essere sostituito da array o hashmaps.

Se le stringhe sono statiche, puoi creare un ENUM. e fare un interruttore su di esso.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top