Альтернатива Switch Case в Java

есть ли какой-либо альтернативный способ реализовать случай коммутатора в Java, Кроме если еще что-то не выглядит хорошо. Набор значений будет там в комбинации, согласно выбору, соответствующий метод должен быть выполнен.

14 ответов


предположительно, вы боретесь с требованием, чтобы случай был постоянным. Как правило, это код запах, но есть вещи, которые вы можете сделать. Возможно, вы захотите поднять и связать с другим вопросом, который подробно объясняет, почему вы пытаетесь переключиться.

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

В приведенном выше примере вы можете сопоставить с "обработчиками", что-то вроде

interface Handler {
    public void doSomething();
}

что затем делает все это превращается в поиск.

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

опять же, это немного запах, поэтому, пожалуйста, разместите вопрос, который иллюстрирует рассуждения.


если у вас есть много операторов switch/case вокруг вашего кода, и они сводят вас с ума.

вы можете выбрать рефакторинг:замена условного оператора полиморфизмом.

предположим, у вас есть часть программного обеспечения, которое используется для сохранения информации на разных устройствах: 4 операции сохранения определены:загрузка, сохранение, удаление, обновление, который может быть реализован N числом механизм сохранения (плоские файлы, сеть, СУБД, XML и т. д.).

ваш код должен поддерживать их все, так что в 4 разных местах, у вас есть это:

до

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

то же самое для сохранения/удаления/обновления

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

и так далее....

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

использование оператора switch / case становится проблематичным:

  • каждый раз, когда вы хотите добавить новый тип, вы должны вставить новый переключатель / корпус в каждый раздел.

  • много раз, некоторые типы похожи, и им не нужен другой переключатель / случай (вы можете каскадировать их )

  • некоторые другие они есть, и иногда они немного отличаются
  • вам даже может потребоваться загрузить другой тип во время выполнения (например, Плагины )

таким образом, рефакторинг здесь должен был бы добавить интерфейс или абстрактный тип и иметь различные типы, реализующие этот интерфейс и делегирующие ответственность перед этим объектом.

таким образом, у вас будет что-то вроде этого:

после

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

и различные реализации

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

и другие типы будут реализованы в соответствии с их логикой. Сеть будет иметь дело с сокетами и потоками, DB будет иметь дело с JDBC, ResultSets и т. д. XML с узлом 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
        }
  }

и, наконец, вам просто нужно делегировать вызовы.

позже:

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

Итак, вам просто нужно создать правильный экземпляр для менеджера сохраняемости в соответствии с типом только один раз. Тогда все призывы разрешаются полиморфизмом. Это одна из ключевых особенностей объектно-ориентированной технологии.

если вы решите, что вам нужен другой менеджер сохраняемости, вы просто создадите новую реализацию и назначите ее классу.

 public WavePersistance implements PersistanceManager {

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

рефакторинг вашего кода для использования полиморфизма может избавиться от необходимости оператора switch. Тем не менее, есть некоторые законные использования для коммутатора, поэтому это зависит от вашей ситуации.


уродливый ряд if,else if,else ?


или можно представить себе своего рода динамический случай переключателя:

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

Я думаю, что "чистый код" имеет хорошую главу в соответствии с переключателем/случаем против if/else.

кроме того: я думаю, имеет смысл решить, можете ли вы уменьшить "шум" и сделать код чище, используя случай переключателя, полиморфизм или даже хороший ol' if/else. Я думаю, что количество дел играет здесь важную роль.


я публикую типичный случай, как Я заменил случай переключателя перечислением.

перед рефакторингом у меня есть перечисление PatternTypes:

public enum PatternTypes {

    ALPHA_CHAR, ALPHANUMERIC_CHAR, ADDITIONAL_CHAR, UNICODE_BMP_CHARS
}

и функции:

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

    }

после рефакторинга PatternTypes изменен на:

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

и функция упростите:

/*
     * 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 считается не дружественным к памяти, поэтому вы можете использовать перечисление для этой цели.

пример:

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

Это избавит вас от написания случая переключателя или операторов if.


для альтернативного оператора переключения, я думаю, что лучшим решением будет использование перечисление. Например: рассмотрим случай ниже:-

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

}

вышеуказанное перечисление можно использовать следующим образом:

EnumExample.OPTION1.execute();

надеюсь, это поможет вам, ребята.


Что вы хотите сделать? Почему Switch-Case недостаточно хорош?

быстрый ответ: используйте if-else


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

?

но я бы не сказал, что это лучше...


как о if (вместе с else if и else) заявление? В то время как switch позволит вам переключаться только с помощью равенства против целых или перечислений типов,if позволяет использовать любую логику.


вы всегда можете заменить выключатель с if-else if-else if-else if..., хотя я не понимаю, зачем вам это нужно. В зависимости от контекста switchs также иногда можно заменить массивами или хэш-картами.


Если строки статические, вы можете сделать перечисление. и включите его.