В настоящее время я работаю над обработчиком протокола для своего проекта на Java. Я пытаюсь создать поддерживаемый и расширяемый API, то есть я не хочу просто жестко кодировать поддерживаемые типы значений.
Я начал разрабатывать «обработчик протокола», который при заданном значении (поддерживаемого типа) может кодировать это значение в соответствии со спецификацией протокола, не беспокоя клиента о деталях процесса перевода.
Различные типы значений, например Строки или целые числа обязательно имеют разные процессы кодирования, но я не хочу, чтобы клиенты беспокоились о хранении ссылок на другой объект для каждого возможного типа, который им может потребоваться кодировать - как я уже сказал, я не хочу, чтобы они беспокоились о деталях.
До сих пор я определил «общий» класс DynamicHandler, который поддерживает коллекцию «определенных» типов StaticHandler:
class DynamicHandler
{
Map<Class, StaticHandler> handlers;
<T> void handle(T value)
{
if (handlers.containsKey(value.class))
handlers.get(value.getType()).handle(value);
}
void <T> register(StaticHandler<T> handler)
{
handlers.put(T.class, handler);
}
}
Идея этого класса заключается в том, что клиент просто передает значение, которое он хочет закодировать, методу handle, а DynamicHandler ищет и делегирует StaticHandler.
interface StaticHandler<T>
{
void handle(T value);
}
Вот пример клиента, который использует эту систему:
class StringHandler implements StaticHandler<String>
{
void handle(String value)
{
...
}
}
DynamicHandler handler = new DynamicHandler();
handler.register(new StringHandler());
handler.handle("Hello World!");
У меня есть два вопроса, на которые я пытаюсь найти ответ самостоятельно:
DynamicHandler.register получить тип T, не имея экземпляра T?Обновлено: поскольку DynamicHandler по сути является картой, хотя и с некоторыми общими уловками, можно ли реализовать ее как: DynamicHandler implements java.util.Map<...,...> (я не совсем уверен, какие типы ключей и значений должны быть здесь).
Я впервые задаю здесь вопрос, поэтому я надеюсь, что я был достаточно ясен для всех вас. Если есть что-то, что, по вашему мнению, требует разъяснения, просто дайте мне знать, и я постараюсь изо всех сил.
Вы можете вызвать getClass() для объекта, переданного в DynamicHandler.handle(), вместо использования obj.class, который не компилируется. Вы предполагаете, что если null передан, не должно быть ничего, что вам нужно было бы обрабатывать, и не будет никаких шансов, что объект соответствует нескольким ключам из карты.
@Jal Мои извинения, на самом деле это была ошибка в моем коде. Я обновил его, чтобы вместо этого использовать getClass (). Я также прояснил свой первый вопрос, чтобы прояснить, что хочу знать, как получить тип T в методе регистрации.
@Ohlec Я отредактировал свой второй вопрос, надеюсь, теперь он стал яснее.
@MattMead, как вы представляете клиента, использующего это как карту? Разве handle() не самый удобный интерфейс для этого?
@Ohlec Я подозреваю, что вы правы в том, что register () и handle () достаточны для моих предполагаемых случаев использования. Я представлял себе что-то вроде register () эквивалентным put (), например. handlerMap.put (String.class, new StringHandler ()) и handle () эквивалентны get (), например. handlerMap.get (String.class). Однако общие параметры для handle () и register () несовместимы с Map.put () и get (). Я думаю, что мне, вероятно, следует реализовать класс как есть на данный момент и при необходимости побеспокоиться об обобщении на Map позже.




- In the DynamicHandler class, how can I get the type of T without having an instance of T?
Обычное решение для этого - передать ему объект Class:
void <T> register(StaticHandler<T> handler, Class<T> clazz)
{
handlers.put(clazz, handler);
}
Как один из примеров этого в широко используемой библиотеке: Gson делает что-то подобное для регистрации сериализаторов JSON для определенных типов с помощью GsonBuilder.registerTypeHierarchyAdapter.
Или с той же идеей добавьте метод в StaticHandler, который дает класс T
@ohlec Спасибо, я сам задавался вопросом об этом, но не был уверен, был ли это лучший или самый распространенный способ сделать это. Для клиента показалось немного избыточным явно указывать тип в качестве параметра, если он неявно указан из T. Я постараюсь сделать это таким образом.
@Sodala Меня тоже интересовал этот подход. Я попробую оба и посмотрю, что мне больше подходит.
@MattMead, безусловно, выглядит избыточным, но из-за стирания типа клиентам действительно нужно явно указывать тип каким-то образом (погода в регистре или в другом методе на самом деле не имеет значения)
@MattMead Я добавил ссылку на пример, в котором Google GSON использует именно этот шаблон для очень похожей цели.
@Ohlec Спасибо, что добавили это, похоже, это будет полезно для меня!
Если значения null не обрабатываются и он спроектирован таким образом, что никогда не будет никакого значения, принадлежащего нескольким классам, вы можете сделать это:
public class DynamicHandler {
Map<Class, StaticHandler> handlers;
public <T> void handle(T value) {
if (value != null) {
handlers.entrySet()
.stream()
.filter(entry ->
entry.getKey().isInstance(value))
.findAny()
.ifPresent(entry ->
entry.getValue().handle(value));
}
}
public void <T> register(StaticHandler<T> handler) {
handlers.put(handler.getHandlingClass(), handler);
}
}
interface StaticHandler<T>
{
void handle(T value);
Class<T> getHandlingClass();
}
public class StringHandler implements StaticHandler<String> {
@Override public void handle(String value) {
...
}
@Override public final Class<String> getHandlingClass() {
return String.class;
}
}
@Jal В настоящее время я планирую игнорировать нулевые значения, кажется, нет смысла ничего «кодировать» для моего предполагаемого варианта использования. Я думаю, что предоставленный вами код - элегантное решение моей проблемы. Я определенно пропустил идею getHandlingClass () для статического обработчика, спасибо!
@MattMead Когда дело доходит до разработки API, определенно нужно учесть много вещей. У каждого подхода есть свои плюсы и минусы. Лучше знать о различных подходах, а затем рассматривать их оттуда.
Не могли бы вы более четко сформулировать свой второй вопрос? Я изо всех сил пытаюсь понять, что именно вы здесь спрашиваете.