#java #oop #design-patterns
#java #ооп #шаблоны проектирования
Вопрос:
У меня есть следующий сценарий, для которого я изо всех сил пытаюсь придумать элегантное решение…
- У меня есть конечная точка, которая подписана на раздел паба, этот конкретный раздел паба содержит 10 различных типов уведомлений / сообщений, которые он может читать.
Ниже приведен интерфейс
public interface Handler {
void handle(String msg);
}
Ниже приведен обработчик событий
public class TeamMemberHandler implements Handler {
@Override
public void handle(String msg) {
}
}
Ниже приведен класс прослушивателя pubsub, в котором я хочу отказаться от использования 10 разных вариантов переключения для каждого события.
public class PubSubListener {
// garbage quick example
@PostMapping("/pubsublistener")
private String pubsublistener(@RequestBody String msg) {
Map<String, Object> map = new Gson.fromJson(msg, HashMap.class);
String event = (String)map.get("rootElement");
// want to move away from this
switch(event){
case "1":
break;
.
.
.
case "10":
break;
default:
break;
}
}
}
Я хотел бы сделать что-то вроде следующего… где у меня есть карта каждого отдельного типа события в строке и связанного с ним обработчика.
Однако в приведенном ниже коде невозможно выполнить handler.handle() . Как я могу добиться этого?
Map<String, Class<? extends Handler>> handlers = new HashMap<>();
handlers.put("TeamMemberEvent", TeamMemberHandler.class);
handlers.put("OtherTypeOfEvent", OtherTypeOfHandler.class);
Class<? extends Handler> handler = handlers.get(event);
handler.handle(msg);
Комментарии:
1. Слабое название. Перепишите, чтобы обобщить вашу конкретную техническую проблему.
Ответ №1:
Я думаю, вы неправильно понимаете шаблон проектирования. Обычно так обнаруживается, что язык постоянно используется для решения более масштабной проблемы, чем обеспечивают встроенные средства языка.
В вашем случае вы хотите использовать a Map
, который является встроенным средством языка, внутри java.util
пакета. Предполагая, что у вас есть Map
тип <String, ? extends Handler>
, который вы можете получить ? extends Handler
, просмотрев значение с String
помощью . Пример этого может выглядеть так:
@PostMapping("/pubsublistener")
private String pubsublistener(@RequestBody String msg) {
Map<String, Object> map = new Gson.fromJson(msg, HashMap.class);
String event = (String)map.get("rootElement");
Handler handler = handlers.get(event);
handler.handle(event);
}
Обратите внимание, что перед использованием этого подхода вам необходимо заполнить Map
с вашими обработчиками и строками, которые будут использоваться для их поиска. Это делается с .put(key, value)
помощью метода Map
, который в вашем случае будет принимать строку в качестве ключа и обработчик в качестве значения.
Комментарии:
1. Как бы мне правильно заполнить эту карту? Это было бы что-то вроде …. map.put(событие, класс ???), но в этом сценарии, если у меня есть OtherTypeOfHandler.java , каким было бы значение в этом случае для карты.
2. @Skrytz Если возможно поместить все желаемые значения в карту сразу, я предлагаю поместить их в карту до ее использования. Обычно мы ожидаем некоторый установочный код в начале программы; прежде чем он начнет обрабатывать запросы. Если содержимое карты меняется со временем, вам нужно будет решить, какой ввод будет запускать то, что попадает в карту, и как сопоставить их вместе.
Ответ №2:
1-й вариант — если обработчик не принимает никаких параметров в конструкторе, вы можете сделать следующее:
private final Map<String, Supplier<Handler>> handlersByEvent = Map.of(
"1", TeamMemberHandler::new,
"2", OtherTypeOfHandler::new
);
затем в ::pubsublistener
:
...
Supplier<Handler> handlerBuilder = handlersByEvent.get(map.get("rootElement"));
Handler handler = handlerBuilder.get();
handler.handle(msg);
2-й вариант — используйте кучу методов вместо классов для обработчиков:
private final Map<String, Consumer<String>> handlersByEvent = Map.of(
"1", this::handleTeamMemberMethod,
"2", this::handleOtherTypeOfMethod
);
затем в ::pubsublistener
:
...
Consumer<String> handler = handlersByEvent.get(map.get("rootElement"));
handler.accept(msg);
Вероятно, есть другие, даже лучшие подходы…