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

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

8 голосов
/ 15 сентября 2009

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

Ответы [ 14 ]

26 голосов
/ 15 сентября 2009

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

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

Допустим, у вас есть часть программного обеспечения, которая используется для сохранения информации на разных устройствах: определены 4 операции сохранения: извлечение, сохранение, удаление, обновление , которое может Реализовано число механизмов сохранения (плоские файлы, сеть, RDBMS, 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 ){
          ....
        }
  }

И другие типы будут реализовывать в соответствии с их логикой. Сеть будет работать с сокетами и потоками, БД будет работать с JDBC, ResultSets и т. Д. XML с узлом и т. Д. И т. Д.

  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 ){
           //  ....
        }
   }
16 голосов
/ 15 сентября 2009

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

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

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

5 голосов
/ 15 сентября 2009

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

1 голос
/ 04 июня 2018

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

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

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

1 голос
/ 10 мая 2017

Hashmap считается не дружественным к памяти, поэтому вы можете использовать Enum для этой цели.

Пример:

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

Это избавит вас от написания Switch Case или if операторов.

1 голос
/ 25 мая 2015

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

перед рефакторингом у меня есть enum 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);

    }
1 голос
/ 15 сентября 2009

Полагаю, у "Чистого кода" есть хорошая глава по ключу / регистру и если / иначе.

Кроме того: я думаю, что имеет смысл решить, сможете ли вы уменьшить «шум» и сделать код чище, используя регистр переключателя, полиморфизм или даже хороший старый if / else. Полагаю, что здесь большое число случаев играет главную роль.

1 голос
/ 15 сентября 2009

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

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);}
         }
     }
1 голос
/ 15 сентября 2009

уродливая серия if,else if,else?

0 голосов
/ 18 апреля 2013

Если строки статические, Вы можете сделать ENUM. и переключиться на это.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...