【问题标题】:Alternative to Switch Case in JavaJava 中 Switch Case 的替代方案
【发布时间】:2010-11-28 09:07:49
【问题描述】:

除了看起来不太好的 if else 之外,还有其他方法可以在 Java 中实现 switch case。一组值会组合在一起,根据选择需要执行相应的方法。

【问题讨论】:

  • 当您说“组合中将包含一组值”时,您是什么意思?
  • 已经问过类似问题stackoverflow.com/questions/126409/…
  • @cdb:那你为什么又问了?以前的答案没有告诉你什么?

标签: java conditional-statements


【解决方案1】:

如果您的代码周围有大量 switch/case 语句,它们会让您发疯。

您可以选择重构:Replace conditional with polymorphism.

假设您有一个用于将信息保存到不同设备的软件:定义了 4 个持久性操作:获取、保存、删除、更新,其中可以通过N种持久化机制(平面文件、网络、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 语句会出现问题:

  • 每次您想添加新类型时,您都必须在每个部分中插入新的开关/案例。

  • 很多时候,有些类型是相似的,它们不需要不同的 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 与节点等。

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

【讨论】:

    【解决方案2】:

    大概您正在努力满足案例保持不变的要求。通常这是一种代码气味,但您可以做一些事情。您可能想提出并链接到另一个详细说明您尝试转换的问题的问题。

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

    再次,它有点味道,所以请发布一个说明推理的问题。

    【讨论】:

    • 如果恒定性很痛苦,您也可以使用枚举。
    • 正在寻找类似的东西。
    • 为什么包含然后获取,您从地图中读取两次。命令注释 = map.get(key); if( null != command ){ ...command.execute(); }
    • 根据实施情况,包含可能不会太贵。知道是否有条目还可以让您以合理可读的方式处理“默认”操作。过早的优化等等。这实际上可能是我在 Perl 中经常这样做以避免自动恢复的结果......
    • 为了让枚举更进一步,您可以使用处理方法设置每个枚举常量,使其成为 value.doSomthing()。
    【解决方案3】:

    重构代码以使用多态性可以消除对 switch 语句的需要。但是,switch 有一些合法用途,因此取决于您的情况。

    【讨论】:

    【解决方案4】:

    一系列丑陋的if,else if,else

    【讨论】:

      【解决方案5】:

      或者可以想象一种动态开关盒:

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

      【讨论】:

        【解决方案6】:

        我猜“清洁代码”根据 switch/case 与 if/else 有一个不错的章节。

        另外:我认为决定是否可以通过使用 switch case、多态甚至是一个好的 if/else 来减少“噪音”并使代码更清晰是有意义的。我猜,案件的数量在这里起主要作用。

        【讨论】:

          【解决方案7】:

          我发布了一个典型案例,我如何用枚举替换 switch case。

          在重构之前我有枚举 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);
          
              }
          

          【讨论】:

          • 我喜欢它,但这不是用大量的 IF 替换 select 吗?新代码的味道不是 IF 吗?
          【解决方案8】:

          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 语句。

          【讨论】:

          • 您能否提供一些参考来支持 Hashmap 被认为对内存不友好的说法?如果可以,请编辑您的答案以将其链接到此处。
          【解决方案9】:

          对于 switch 语句的替代,我认为最好的解决方案是使用 枚举。例如:考虑以下情况:-

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

          希望这对你们有帮助。

          【讨论】:

            【解决方案10】:

            你想做什么?为什么 Switch-Case 不够好?

            快速的答案是:使用 if-else

            【讨论】:

              【解决方案11】:
                  if () {}
                  else if () {}
              ...
                  else if () {}
              

              ?

              但我不会说它更好......

              【讨论】:

                【解决方案12】:

                if(连同else ifelse)声明怎么样? switch 只允许您使用相等来切换整数或枚举类型,if 允许您使用任何布尔逻辑。

                【讨论】:

                • char、byte、short也可以用在switch中。
                • 是的,但它们也可以安全(自动)转换为 int
                【解决方案13】:

                您总是可以用if-else if-else if-else if... 替换开关,但我不明白您为什么要这样做。根据上下文,switchs 有时也可以替换为数组或哈希图。

                【讨论】:

                  【解决方案14】:

                  如果字符串是静态的,您可以创建一个 ENUM。 并打开它。

                  【讨论】:

                    猜你喜欢
                    • 2021-04-20
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2021-12-07
                    • 1970-01-01
                    • 2013-04-02
                    • 1970-01-01
                    • 2011-01-19
                    相关资源
                    最近更新 更多