Способы избежать, если-иначе, корпуса выключателя в заводском дизайне

#java #reflection #factory #checkstyle #cyclomatic-complexity

Вопрос:

Я разрабатываю модуль проверки. Он содержит 100 кодов ошибок(т. е. errcd_01, errcd_02,..,errcd_100), подлежащих проверке. При вводе я получаю конкретный код ошибки(т. Е. errcd_01) из более чем 100. Модуль должен выполнить проверку для этого конкретного кода ошибки.

Я использую фабричный шаблон.

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

Теперь из — за нескольких ошибок if/else внутри Factory класса ErrorValidationFactory я получаю пару ошибок CI/CD при выполнении чистой установки mvn.
например, [Длина метода] — стиль проверки, правило:цикломатическая сложность- PMD.

Итак, есть ли способ, которым я могу заменить, если/иначе, процесс принятия решений в случае переключения внутри фабрики, который не вызывает выше ошибок CI/CD в Java?

Примечание : Если возможно, я хотел бы избежать размышлений

Комментарии:

1. Создайте a Map<String, validateErrCd> и заполните его записями, например validators.put("errcd_100", new validateErrCd_100()) , затем .get() верните свой валидатор по коду.

2. @VLAZ, если подумать, это не сработает, потому что 2 вызова с одним и тем же ключом вернут один и тот же экземпляр, что может быть не тем, что нужно OP, судя по показанному ими коду (если, конечно, вы не создадите карту внутри getValidation )

3. @FedericoklezCulloca, тогда это просто Map(String, Supplier<validateErrCd>) с validators.put("errcd_100", validateErrCd_100::new) , верно?

4. @VLAZ да, вот что говорит дан1-й ответ (сейчас):)

Ответ №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 или ()->new SomeClass() означает, что для этого будет использоваться конструктор класса.

Это позволяет создавать экземпляры позже.

Если вы хотите создать Map только один раз, вы можете создать его static и заполнить в статическом инициализаторе.

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

Комментарии:

1. Поскольку использовался исходный код equalsIgnoreCase , эквивалентное решение будет использовать new TreeMap<>(String.CASE_INSENSITIVE_ORDER) вместо new HashMap<>() . Хотя спорно, имеет ли здесь смысл сопоставление без учета регистра.

2. Вы также можете упростить поиск, чтобы return creators.getOrDefault(errorCode, () -> null).get();

3. Спасибо @dan1st, это сработало, чтобы избежать правила:цикломатическая сложность — PMD. Однако, поскольку мне все равно придется вводить записи для каждого errcd creators.put("errcd_100",validateErrCd_100::new); , это все равно приведет к сбою [MethodLength] — checkstyle. Есть ли способ создать его с помощью цикла, если у меня есть все errcd(в данном случае от 1 до 100), присутствующие в каком-либо списке?

4. @Holger, в настоящее время мы можем игнорировать чувствительность к регистру для этого случая.

5. @Data_Geek похоже, что ваши конкретные классы существуют только для реализации метода validateErrCd интерфейса и не имеют (изменяемого) состояния. Итак, вам действительно нужно создавать их getValidation экземпляры или было бы возможно использовать один элемент для каждого типа? Если это так, просто реализуйте их все как enum тип, и вы получите возможность перечислять их (и даже искать их по имени) бесплатно.