【问题标题】:Ways to Avoid if-else, switch-case in Factory design pattern在工厂设计模式中避免 if-else、switch-case 的方法
【发布时间】:2021-08-20 22:44:02
【问题描述】:

我正在设计一个验证模块。它有 100 个错误代码(即 errcd_01、errcd_02、..、errcd_100)需要验证。在输入中,我得到一个高于 100 的特定错误代码(即 errcd_01)。 模块应该对特定的错误代码进行验证。

我正在使用工厂模式。

/* Interface */
public interface validateErrCd {
   void check_errcd();
}

/* Concrete classes implementing the same interface */
public class validateErrCd_01 implements validateErrCd {

   @Override
   public void check_errcd() {
      //business logic related to errcd_01
   }
}

public class validateErrCd_02 implements validateErrCd {

   @Override
   public void check_errcd() {
      //business logic related to errcd_02
   }
}
.
.
.
public class validateErrCd_100 implements validateErrCd {

   @Override
   public void check_errcd() {
      //business logic related to errcd_100
   }
}

/* Factory */
public class ErrorValidationFactory {
    
   //use check_errcd method to get object of type shape 
   public validateErrCd getValidation(String errorCode){
      if(errorCode == null){
         return null;
      }     
      if(errorCode.equalsIgnoreCase("errcd_01")){
         return new validateErrCd_01();
         
      } else if(errorCode.equalsIgnoreCase("errcd_02")){
         return new validateErrCd_02();
         
      } ..
       .......
      else if(errorCode.equalsIgnoreCase("errcd_100")){
         return new validateErrCd_100();
      }
      else {
           return null;
      }
   }
}

/* I am using the Factory to get object of concrete class by passing an specific error code to be validated (i.e. "errcd_01"). */
public class FactoryPatternDemo {

   public static void main(String[] args) {
      ErrorValidationFactory errorFactory = new ErrorValidationFactory();

      //get an object of validateErrCd_01 and call its check_errcd method.
      validateErrCd errcd01 = errorFactory.getValidation("errcd_01");

      //call check_errcd method of validateErrCd_01
      errcd01.check_errcd();
   }
} 

现在由于 Factory 类 ErrorValidationFactory 中有多个 if/else,我在执行 mvn clean install 时遇到了几个 CI/CD 错误。 例如[MethodLength] - checkstyle, Rule:CyclomaticComplexity - PMD。

那么有没有一种方法可以替换工厂内部的 if/else、switch case 类型的决策,它不会在 Java 中触发上述 CI/CD 错误?

注意:如果可能的话,我想避免反思

【问题讨论】:

  • 创建一个Map<String, validateErrCd> 并用validators.put("errcd_100", new validateErrCd_100()) 之类的条目填充它,然后.get() 通过代码返回您的验证器。
  • @VLAZ 再三考虑,这是行不通的,因为使用相同键的 2 个调用将返回相同的实例,从他们显示的代码来看,这可能不是 OP 需要的(除非,也就是你在getValidation里面创建地图)
  • @FedericoklezCulloca 那么它只是一个Map(String, Supplier<validateErrCd>)validators.put("errcd_100", validateErrCd_100::new) 对吧?
  • @VLAZ 是的,这就是 dan1st answer(现在)所说的 :)

标签: java reflection factory checkstyle cyclomatic-complexity


【解决方案1】:

你可以使用Map:

public class ErrorValidationFactory {
    private Map<String,Supplier<validateErrCd>> creators=new HashMap<>();
    public ErrorValidationFactory(){
        creators.put("errcd_100",validateErrCd_100::new);
        //Same for others
    }
   //use check_errcd method to get object of type shape 
   public validateErrCd getValidation(String errorCode){
        if(errorCode == null){
           return null;
        }
        return creators.getOrDefault(errorCode,()->null);
   }
}

Supplier 是一个函数式接口,其中包含一个返回对象的方法。 SomeClass::new()-&gt;new SomeClass() 表示将使用该类的构造函数。

这允许稍后创建实例。

如果您只想创建一次 Map,可以将其设为 static 并将其填充到静态初始化程序中。

但是,如果您真的想动态获取构造函数,则需要使用反射。

【讨论】:

  • 由于原始代码使用equalsIgnoreCase,等效的解决方案将使用new TreeMap&lt;&gt;(String.CASE_INSENSITIVE_ORDER)而不是new HashMap&lt;&gt;()。不过,不区分大小写的匹配在这里是否有意义仍有争议。
  • 您还可以将查找简化为return creators.getOrDefault(errorCode, () -&gt; null).get();
  • 感谢@dan1st 它可以避免 Rule:CyclomaticComplexity - PMD。但是,由于我仍必须将每个 errcd 的条目放在creators.put("errcd_100",validateErrCd_100::new); 中,它仍然会给出 [MethodLength] - checkstyle 失败。如果我在某个列表中存在所有 errcd(即在这种情况下为 1 到 100),我们是否可以通过循环创建它?
  • @Holger,我们目前可以忽略这种情况的区分大小写。
  • @Data_Geek 看起来你的具体类只存在于实现validateErrCd 接口方法并且不携带(可变)状态。那么你真的需要在getValidation 中实例化它们还是每个类型都有一个单例可行?如果是这种情况,只需将它们全部实现为 enum 类型,您就可以免费枚举它们(甚至按名称查找它们)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-01-27
  • 2011-11-20
  • 1970-01-01
  • 1970-01-01
  • 2023-02-20
  • 2015-06-10
相关资源
最近更新 更多