Search code examples
javareflectionfactorycheckstylecyclomatic-complexity

Ways to Avoid if-else, switch-case in Factory design pattern


I am designing a validation module. It has 100 error codes(i.e. errcd_01, errcd_02,..,errcd_100) to be validated. In input I am getting a specific error code(i.e. errcd_01) out of above 100. Module should perform validation for that specific error code.

I am using factory pattern.

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

Now due to multiple if/else inside Factory class ErrorValidationFactory, I am getting couple of CI/CD errors while performing mvn clean install. e.g. [MethodLength] - checkstyle, Rule:CyclomaticComplexity - PMD.

So is there a way I can replace if/else, switch case kind of decision making inside factory which does not trigger above CI/CD errors in Java?

Note : If possible I would like to avoid reflection


Solution

  • You could use a 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 is a functional interface that contains a method returning an object. SomeClass::new or ()->new SomeClass() means that the constructor of the class will be used for that.

    This allows to to create the instances later.

    If you want to create the Map only once, you can make it static and populate it in a static initializer.

    However, if you really want to dynamically get the constructors, you would need to use reflection.