Как динамически загрузить шаблон фабрики с карты?

У меня есть различные типы событий, как показано ниже. В общем, у меня больше событий.

EventA
EventB
EventC
EventD
EventE

Ниже приведен код, в котором я получу каждый тип событий:

  public void run() {
    try {
      while (!closed.get()) {
        ConsumerRecords records = consumer.poll(10000);
        // Handle new records
        for (ConsumerRecord<String, String> record : records) {
          GenericRecord payload = decoder.decode(record.value());
          String eventType = String.valueOf(payload.get("eventType"));
          String eventMapHolder = String.valueOf(payload.get("eventMapHolder"));
          Map<String, String> eventHolder = parseStringToMap(eventHolder);
          // here eventType will "EventA", "EventB" and so on
          // pass eventType to get the individual factory for that event type
          // and then  pass eventHolder to a particular method of that factory class which calls appropriate Query method.
        }
      }
    } catch (Exception e) {
        // log error
    }
  }

Для каждого события мне нужно выполнять разные запросы к базе данных, но, по крайней мере, для большинства из них мне нужно выполнить один запрос к базе данных, который является общим для всех из них. Например:

EventA  -----> QueryA, QueryB
EventB  -----> QueryA
EventC  -----> QueryA, QueryC
EventD  -----> QueryA, QueryD
EventE  ----->

Ниже будет поток:

                    EventA  ----->  QueryA, QueryB
                    EventB  ----->  QueryA
eventHolder         EventC  ----->  QueryA, QueryC
                    EventD  ----->  QueryA, QueryD
                    EventE  ----->  
  • Итак, скажем, я получаю «EventA» как тип события в коде, который у меня есть в моем вопросе, затем я передам карту eventHolder методу (давайте назовем это как QueryA), и в этом методе я выполню некоторую операцию на этой карте и вернусь новая Карта. И затем эта новая карта будет передана другому методу (давайте назовем этот QueryB), а затем снова мы выполним какую-то операцию над этим, а затем вернем другую карту, а затем эта карта вернется к вызывающему.
  • Теперь предположим, что «EventB» приходит как тип события в коде, который у меня есть в моем вопросе, затем я передам карту eventHolder методу (давайте назовем это как QueryA), и в этом методе я выполню некоторую операцию на этой карте и верну новая Карта. И затем эта карта будет передана обратно вызывающему абоненту.
  • Теперь предположим, что «EventE» приходит как тип события в коде, который у меня есть в моем вопросе, тогда я передам карту eventHolder классу EventE, и, поскольку в этом случае нам ничего не нужно делать, я передам эту карту как есть обратно к звонящему.

Вопрос:

Какой шаблон проектирования я должен использовать для такого рода проблем? Я хочу избежать использования if/else или переключателя здесь, потому что в целом у меня много типов событий, поэтому я думаю о создании для них отдельной фабрики, которую мы можем динамически загружать во время выполнения, передавая значение eventType.

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

Как я могу использовать здесь абстрактный фабричный шаблон для достижения этой цели, если это правильный шаблон проектирования для этой проблемы? Я хочу передать "eventType" в качестве ключа фабрике, которая вернет экземпляр этого типа события. Итак, скажем, я передам "EventA" фабрике, а затем она вернет класс фабрики для "EventA", а затем мы вызовем определенный метод в этом классе, который внутренне вызывает метод QueryA, а затем мы вызовем метод QueryB, и в конце он вернет карту, которая Я распечатаю его в приведенном выше коде. И аналогично для других типов событий.

Я кое-что подумал на эту тему: я не уверен, возможно ли это вообще с фабричным шаблоном или может быть какой-то другой шаблон проектирования. (Мои соглашения об именах могут быть перепутаны, я просто придумал эти имена ниже)

EventFactory eventFactory = EventTypeFactory.getInstance().getFactory(eventType);
Map<String, String> holder = eventFactory.execute(eventHolder);

person john    schedule 11.12.2016    source источник
comment
Я знал очень крутой способ сделать это без использования if-else или switch, но не могу вспомнить. Может быть, утром после хорошего сна.   -  person uylmz    schedule 11.12.2016
comment
Какова цель возвращенной карты?   -  person Tony    schedule 11.12.2016


Ответы (3)


Ну, в этом вопросе есть 2 основные вещи, которых нужно достичь.

  • Внедряйте и повторно используйте пользовательскую логику запросов.
  • Динамически сопоставлять строку eventType со связанными действиями запроса.

Прежде всего, я бы предпочел, чтобы вы приняли вариант Interpreter узор. Так это будет выглядеть так.

public interface Query{
  public Map<String, String> execute(Map<String, String> map);
} 

@Singleton
public class QueryA implements Query{
  @Override
  public Map<String, String> execute(Map<String, String> map){
    //queryA logic.
  }
}     

public class CustomQuery implements Query{
  List<Query> queryList;

  public void CustomQuery(List<Query> queries){
    this.queryList = queries;
  }

  public void CustomQuery(Query query1, Query query2){
    this.queryList = new ArrayList();
    queryList.add(query1);
    queryList.add(query2);
  }

  @Override
  public Map<String, String> execute(Map<String, String> map){
    if(queryList == null){
         return map;
    }

    Map<String, String> eventMap = map;
    for(Query query: queryList){
        eventMap = query.execute(eventMap);
    }
    return eventMap;
  }
}

Обратите внимание, что каждый дочерний класс запроса (например, QueryA) должен быть Singleton, чтобы избежать использования памяти. Используя выше, мы можем иметь пользовательские действия запроса.

пример: QueryAB (требуется событиеA)

@Inject
QueryA queryA;
@Inject
QueryB queryB;
Query queryAB = new CustomQuery(queryA, queryB);

Теперь у вас может быть Map<String, Query> eventToQueryMap, который динамически сопоставляет ваш EventType в необходимые QueryLogic.

Ex:

Map<String, Query> eventToQueryMap = new HashMap();
eventToQueryMap.put("EventA", queryAB);
eventToQueryMap.put("EventB", queryA);
//like above you can add for others too.

Затем всякий раз, когда вы получаете свою строку eventType, все, что вы хотите сделать, это.

Map<String, String> eventMap = eventToQueryMap.get(eventType).execute(eventHolder);

Это набросок решения основной проблемы, с которой вы столкнулись. Вы можете добавить настройки в свой собственный. :))

person Supun Wijerathne    schedule 11.12.2016

Я не уверен, что понял вашу точку зрения. Вам нужен фабричный шаблон только один раз, чтобы инициализировать объект, обрабатывающий ваше событие. Фабрика — это порождающий паттерн, который не имеет ничего общего с выполнением запросов. Так что в основном ваш подход частично правильный:

Object o = EventTypeFactory.getInstance().getFactory(eventType);

Это та точка, где можно было бы использовать фабрику. Я специально использую Object как тип, так как сейчас мы собираемся обсудить, какой шаблон лучше всего подходит для «обработки событий». В первую очередь это должен быть поведенческий паттерн. Я бы сказал, что шаблон Command может пригодиться. Однако вам также может понадобиться шаблон Iterator для представления потенциально разного количества запросов для каждого события.

Тем не менее, делать это таким образом для меня немного излишне. Я думаю, что простой шаблон метода шаблона также приведет к тому, что вам нужно. Однако это зависит от того, что вы хотите сделать с результатом запроса. Вот пример шаблонного метода:

public abstract class EventHandler {
   public abstract Collection<String> getQueries();

   public Map<String, String> handleEvent(Map<String, String> eventData) {
      Map<String, String> result = new HashMap<>();

      for(String query : getQueries()) {
         // execute the query and merge the query result into the result map
      }

      return result;
   }
}

public class EventAEventHandler extends EventHandler {
   public abstract Collection<String> getQueries() {
      List<String> queries = new ArrayList<>();

      queries.add("YourQueryHere");

      return queries;
   }
}

Чтобы обработать событие:

EventHandler handler = EventTypeFactory.getInstance().getFactory(eventType);
Map<String, String> holder = handler.execute(eventMap);

Если вам нужно, чтобы каждая Карта для каждого запроса возвращалась отдельно, вы можете изменить тип возвращаемого значения метода шаблона на List<Map<String, String>>.

person RoK    schedule 11.12.2016
comment
давайте обсудим здесь. Меня немного смущает этот подход. Я объясню вам, что именно я пытаюсь сделать. - person john; 11.12.2016

Насколько я вас понимаю, заводской шаблон вам вроде не нужен. Вы можете просто иметь перечисление, подобное следующему:

public enum Event {

  EventA {
    @Override
    Map<String, String> executeQuery() {
      final Map<String, String> map = super.executeQuery();
      executeQueryB();
      return map;
    }

    private void executeQueryB() {

    }
  },
  EventB,
  EventC,
  EventD,
  ;

  Map<String, String> executeQuery() {
  }

}

Достаточно использовать следующее:

return Event.valueOf("EventA").executeQuery();
person Tony    schedule 11.12.2016
comment
эй ты рядом? Я немного смущен этим подходом. Я нахожусь в этом чате комнате. Давайте обсудим здесь. - person john; 11.12.2016