Pregunta

¿Hay alguna forma alternativa de implementar un caso de conmutación en Java que no sea si no se ve bien? Un conjunto de valores estará allí en combinación, de acuerdo con la selección que se debe ejecutar el método correspondiente.

¿Fue útil?

Solución

Presumiblemente estás luchando con el requisito de que el caso sea constante. Por lo general, esto es un olor a código, pero hay cosas que puede hacer. Es posible que desee plantear y vincular a otra pregunta que detalla por qué está tratando de cambiar.

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

En el ejemplo anterior, es posible que desee asignar a 'controladores', algo así como

interface Handler {
    public void doSomething();
}

que luego hace que todo esto se convierta en una búsqueda.

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

Nuevamente, es un poco oloroso, así que publique una pregunta que ilustre el razonamiento.

Otros consejos

Si tiene muchas declaraciones de cambio / caso alrededor de su código y lo están volviendo loco.

Puede optar por la Refactorización: Reemplazar condicional por polimorfismo.

Supongamos que tiene un software que se utiliza para guardar información en diferentes dispositivos: se definen 4 operaciones de persistencia: buscar, guardar, eliminar, actualizar , que podría implementarse mediante un número N de mecanismo de persistencia (archivos planos, red, RDBMS, XML, etc.).

Su código debe admitirlos a todos, por lo que en 4 lugares diferentes tiene el siguiente:

ANTES

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 mismo para guardar / eliminar / actualizar

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

Y así sucesivamente ...

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

Usar la instrucción switch / case se vuelve problemático:

  • Cada vez que desee agregar un nuevo tipo, debe insertar un nuevo interruptor / caso en cada sección.

  • Muchas veces, algunos tipos son similares y no necesitan un interruptor / carcasa diferente (puede conectarlos en cascada)

  • Algunos otros son, y algunas veces difieren ligeramente
  • Puede que incluso necesite cargar un tipo diferente en tiempo de ejecución (como complementos)

Entonces, la refactorización aquí sería agregar una interfaz o tipo abstracto y hacer que los diferentes tipos implementen esa interfaz y deleguen la responsabilidad a ese objeto.

Entonces tendría algo como esto:

AFTER

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

Y diferentes implementaciones

  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 ){
          ....
        }
  }

Y los otros tipos se implementarían de acuerdo con su lógica. La red se ocuparía de los sockets y las secuencias, la DB se ocuparía de JDBC, ResultSets, etc. XML con el 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
        }
  }

Y finalmente solo tiene que delegar las invocaciones.

Más tarde:

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

Entonces, solo tiene que crear la instancia correcta para el administrador de persistencia de acuerdo con el tipo solo una vez. Entonces todas las invocaciones se resuelven por polimorfismo. Esa es una de las características clave de la tecnología orientada a objetos.

Si decide que necesita otro administrador de persistencia, simplemente cree la nueva implementación y asigne a la clase.

 public WavePersistance implements PersistanceManager {

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

Refactorizar su código para usar el polimorfismo podría eliminar la necesidad de una declaración de cambio. Sin embargo, hay algunos usos legítimos para cambiar, por lo que depende de su situación.

una serie fea de if, else if, else ?

o uno podría imaginar una especie de caja de interruptor dinámico:

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

Supongo que "Código limpio" tiene un buen capítulo según switch / case vs. if / else.

Además: creo que tiene sentido decidir si puede reducir el "ruido" y haga que el código sea más limpio mediante el uso de mayúsculas y minúsculas, polimorfismo o incluso un buen if / else El número de casos juega un papel importante aquí, supongo.

Publico un caso típico de cómo reemplacé la caja del interruptor con enum.

antes de refactorizar tengo enum PatternTypes :

public enum PatternTypes {

    ALPHA_CHAR, ALPHANUMERIC_CHAR, ADDITIONAL_CHAR, UNICODE_BMP_CHARS
}

y función:

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

    }

Después de refactorizar PatternTypes modificado a:

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

y la función se simplifica a:

/*
     * 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 se considera que no es amigable con la memoria, por lo que puede usar Enum para este propósito.

Ejemplo:

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
}}

Esto le evitará que escriba Switch Case o if declaraciones.

Para una alternativa para cambiar la declaración, creo que la mejor solución será utilizar una enumeración . Por ejemplo: considere el siguiente caso: -

    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();

}

La enumeración anterior se puede usar de la siguiente manera:

EnumExample.OPTION1.execute();

Espero que esto los ayude.

¿Qué quieres hacer? ¿Por qué no es suficiente Switch-Case?

La respuesta rápida es: use if-else

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

?

Pero no diría que es mejor ...

¿Qué tal una declaración if (junto con else if y else )? Mientras que switch solo le permitirá cambiar usando igualdad contra tipos enteros o Enum, if le permite usar cualquier lógica booleana.

Siempre puedes reemplazar un interruptor con if-else if-else if-else if ... , aunque no veo por qué quieres hacerlo. Dependiendo del contexto, los switch s a veces también pueden ser reemplazados por arrays o hashmaps.

Si las cadenas son estáticas, puede hacer un ENUM. y enciéndelo.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top