I have various types of events as shown below. In general I have more events.
EventA
EventB
EventC
EventD
EventE
Below is the code where I will get each event types:
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
}
}
For each event, I need to perform different database queries but for most of them atleast, I need to perform one database query which is common for all of them. For example:
EventA -----> QueryA, QueryB
EventB -----> QueryA
EventC -----> QueryA, QueryC
EventD -----> QueryA, QueryD
EventE ----->
Below will be the flow:
EventA -----> QueryA, QueryB
EventB -----> QueryA
eventHolder EventC -----> QueryA, QueryC
EventD -----> QueryA, QueryD
EventE ----->
Question:
Which design pattern I should use for this kind of problem? I want to avoid using if/else or switch here bcoz in general I have lot of event types so thinking of making an individual factory for them which we can load it at runtime dynamically by passing eventType value.
I know all the eventType beforehand so I can statically initialize all the factories by putting it in a map and then once the eventType comes while we are running the code, I can load individual factories from the map.
How can I use abstract factory pattern here to accomplish this if this is the right design pattern for this problem? I want to pass "eventType"
as the key to a factory which will return instance of that event type. So let's say I will pass "EventA"
to the factory and then it will return factory class for "EventA"
and then we will call a particular method on that class which internally calls QueryA method and then we call QueryB method and at the end it will return Map which I will print it out in the above code. And Similarly for other event types.
I was thinking something on this ground: I am not sure whether this is possible at all with the factory pattern or may be some other design pattern. (My naming conventions might be messed up, I just came up with those below names)
EventFactory eventFactory = EventTypeFactory.getInstance().getFactory(eventType);
Map<String, String> holder = eventFactory.execute(eventHolder);
Well, there are 2 main things to achieve in this question.
For the first thing I prefer you adopt a variation of Interpreter pattern. So it would look like this.
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;
}
}
Note that each child query class (ex: QueryA) should be Singleton to avoid memory foot-print. Using above we can have custom query actions.
ex: QueryAB (needs for eventA)
@Inject
QueryA queryA;
@Inject
QueryB queryB;
Query queryAB = new CustomQuery(queryA, queryB);
Now you can have a Map<String, Query> eventToQueryMap
which dynamically maps your EventType into the necessary QueryLogic(s).
Ex:
Map<String, Query> eventToQueryMap = new HashMap();
eventToQueryMap.put("EventA", queryAB);
eventToQueryMap.put("EventB", queryA);
//like above you can add for others too.
Then whenever you receive your eventType String what you all want to do is.
Map<String, String> eventMap = eventToQueryMap.get(eventType).execute(eventHolder);
This is the outline solution for the main problem you have. You can add customization in your own. :))