Шаблон проектирования ООП Java для следующего сценария

#java #oop #design-patterns

#java #ооп #шаблоны проектирования

Вопрос:

У меня есть следующий сценарий, для которого я изо всех сил пытаюсь придумать элегантное решение…

  1. У меня есть конечная точка, которая подписана на раздел паба, этот конкретный раздел паба содержит 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);
  

Вероятно, есть другие, даже лучшие подходы…