Обновление значения для существующей константы перечисления на ходу в Drools

#java #enums #drools

#java #перечисления #drools

Вопрос:

У меня есть приведенная ниже структура перечисления, для которой я должен изменить значение 2-го аргумента THREAT . Приложение почти разработано и сильно зависит от типов перечислений, и тип не может быть изменен (из-за большого количества переменных перечисления). При перезапуске приложения мне нужны значения по умолчанию. Могу ли я каким-либо образом изменить значение THREAT на лету?

 enum TraceLevel {
   APP_DOS("as", ""),
   APP_DOS1("as", ""),
   APP_DOS2("as", ""),
   APP_DOS3("as", ""),
   APP_DOS4("as", "");
   String NAME;
   String THREAT;

   private TraceLevel(String name, String threat) {
      this.NAME = name;
      this.THREAT = threat;
   }
}
  

Обновление 1
в зависимости от комментариев, я думаю, мне следует немного обновить проблему. Я в основном работаю над механизмом правил drools, где у меня есть константы enum. Объявление там на самом деле не похоже на Java. Итак, я не могу понять, как мне это сделать. Вот конкретный шаблон drools.

 declare enum AttackCategory
APP_DOS("as", ""),
APP_DOS1("as", ""),
APP_DOS2("as", ""),
APP_DOS3("as", ""),
APP_DOS4("as", "");
value : String
threat: String

end
  

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

1. Да, есть способ: использовать сеттер. Вы что-нибудь пробовали?

2. Очень плохая идея иметь изменяемые поля в перечислении, но ничто в языке Java не мешает вам это делать. Вы можете просто добавить параметр и изменить поле.

3. Это звучит как XY-проблема. Чего вы на самом деле пытаетесь достичь здесь? изменение состояния перечислений почти всегда является очень, очень плохой идеей.

4. Я только что обновил вопрос. Не могли бы вы взглянуть сейчас? @f1sh

5. Вы только что узнали, что хотите изменить внутренние компоненты константы перечисления, что противоречит концепции «константы». Это требует либо тщательного рефакторинга (даже если «приложение почти разработано»), либо больших проблем в долгосрочной перспективе.

Ответ №1:

Как говорится в очень многих комментариях к вопросам, вы пытаетесь использовать enum таким образом, для которого он не предназначен или не предназначен. Как и в большинстве случаев, когда это происходит, вы, вероятно, можете заставить его работать, но у вас возникнут дополнительные проблемы. Правильный ответ — вернуться к чертежной доске, как говорится, и придумать решение, которое не пытается заставить enum иметь поведение класса.


(Неправильный) способ перечисления

Чтобы изменить значение в перечислении, добавьте сеттеры в свое объявление. В Java это делается следующим образом:

 enum TraceLevel {
   APP_DOS("as", ""), ...;
   String NAME;
   String THREAT;

   private TraceLevel(String name, String threat) {
      this.NAME = name;
      this.THREAT = threat;
   }

  public String getThreat() { return this.NAME; }
  public void setThreat(String name) { this.NAME = name; }
}
  

(Также незначительный комментарий — переменные для ‘name’ и ‘threat’ не должны быть полностью прописными. Они должны следовать соглашениям об именовании для обычных переменных.)

Если, с другой стороны, вы используете объявления типов Drools, вы не создаете методы в этих структурах, а вместо этого Drools генерирует геттеры и сеттеры, следуя соглашениям bean. Как вы указываете, эти соглашения не генерируют такие методы в перечислениях (что должно было быть намеком на то, что вы пытаетесь использовать их неправильно.)

Поэтому, если вы должны использовать перечисления, вы должны объявить их в Java.


Альтернативный подход: классы

Ваш вариант использования, похоже, просто прославленные константы, так почему бы не использовать это в качестве альтернативного подхода? Объявите класс с тремя строковыми переменными (имя, поток, идентификатор.) Затем в правиле вставьте их все в рабочую память с примененными значениями по умолчанию. Когда вам нужно их использовать, вы можете обновлять их в памяти по мере необходимости.

 declare TraceLevel
  id: String
  name: String
  threat: String
end

rule "Prepare default trace levels"
when
  not( TraceLevel() )
then
  insert( new TraceLevel("APP_DOS", "as", "") ) // equivalent to TraceLevel.APP_DOS
  insert( new TraceLevel("APP_DOS2", "as", "") )
  // etc.
end

rule "Example rule which needs to update a Threat value"
when
  $traceLevel: TraceLevel( id == "APP_DOS" )
then
  modify( $traceLevel ) { threat = "new threat value" }
end
  

Первое правило, «Подготовить уровни трассировки по умолчанию», вставляет уровни по умолчанию в рабочую память. На данный момент рабочая память содержит набор этих объектов, которые функционально идентичны вашим значениям enum. Поскольку они находятся в рабочей памяти, теперь они доступны для любых последующих правил.

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

Во втором примере правила показано, как вы будете обновлять значение «угроза» с помощью modify действия правила.

Во всех случаях я добавил поле «id», чтобы вы могли идентифицировать конкретные экземпляры уровня трассировки. Это эквиваленты имен перечислений на уровне трассировки (например. Уровень трассировки.APP_DOS -> id = «APP_DOS»). Второй пример показывает, как вы могли бы использовать этот идентификатор для получения конкретного экземпляра уровня трассировки.