2015-03-06 3 views
6

Мой вопрос заключается в том, чтобы реализовать различные типы поведения для разных сообщений максимально расширительно. Я знаю о шаблоне посетителя, я знаю о двойной отправке, но я не могу найти решение, которое удовлетворяет мне (не в пределах java как минимум).Двунаправленная расширяемая иерархия с Java

Моя ситуация выглядит следующим образом:

У меня есть иерархия сообщения:

Message Hierarchy

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

Router-Interface Hierarchy

, которые я хотел бы реализовать похожее на это:

Implementation

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

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

CompositeRouter comp = new AllRouter(...//new Router instances); 
MessageBase msg = new DerivedMessage(); 
msg.process(comp); 

приведет к Java выбора перегрузка <runtime message-type>.process(Router)

во время компиляции, которое во время выполнения вызывается для соответствующего объекта маршрутизатора. Поэтому я не могу выбрать правильные вызовы process() во время компиляции. Я также не могу сделать это наоборот, потому что comp.route(msg)

будет разрешен к <dynamic router-type>.route(MessageBase).

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

Есть ли способ реализовать это так, чтобы как Message, так и Router были расширяемы или это безнадежно, учитывая текущие java-функции?

Edit 1:

Что-то я забыл упомянуть о том, что у меня есть 4 или 5 других ситуаций, которые в значительной степени так же, как Router -hierarchy, так что я как бы хочу, чтобы избежать отражения для метода-поиска , потому что я боюсь затрат времени исполнения.

Ответ на комментарий:

  1. @ предположения aruisdante в отношении предложения @ бота правильно. I не может переопределить, потому что я бы потерял во время выполнения тип MessageBase, если я переопределяю маршрут (MessageBase).

  2. @aruisdante и @geceo: Я знаю, что могу это сделать - это то, что я имел в виду с «переключением» (MessageBase имеет поле MessageType) - но у меня есть 11 фактических классов сообщений и ~ 6 местоположений в код, где мне нужен , так это было бы ОГРОМНОЙ болью, а также по обслуживанию.

+0

Возможно, я неправильно понял вашу проблему, но почему вы перегружаете методы вместо их «переопределения»? Зачем передавать сообщения как параметры? Почему бы не использовать MessageBase как зависимость в Router? – CKing

+1

@bot Я думаю, что намерение OP состоит в том, чтобы разрешать обработку различных сообщений разными способами (перегрузка) и * также * позволять различным классам обрабатывать разные сообщения по-разному (переопределять), но не требовать от каждого класса в иерархии чтобы обеспечить все перегрузки. Проблема, с которой они сталкиваются, по существу, заключается в том, что система работает так, как предусмотрено, и сообщение, и маршрутизатор должны знать конкретный тип времени выполнения (сообщение должно знать тип маршрутизатора, маршрутизатор должен знать тип сообщения). – aruisdante

+0

Я как-то написал несколько ограниченных ["generic function" stuff] (https://github.com/deterministic-arts/DartsJavaDispatch) для Java. Обратите внимание, что код не является ни проверенным, ни поддерживаемым, а тем более документированным или поддерживаемым. Я не рекомендую использовать его в производственных средах, но, возможно, вы можете использовать его, чтобы получить вдохновение ... – Dirk

ответ

1

В Scala (другой язык JVM), это звучит как прецеденте для классов типа:

http://danielwestheide.com/blog/2013/02/06/the-neophytes-guide-to-scala-part-12-type-classes.html

Это не возможно в Java, хотя есть некоторые экспериментальные библиотеки для него, если вы google "классы типа Java".

+0

Извините, но это должна быть Java. Я думаю, что я знаю хак, чтобы он тоже работал на C++, но так же, как и Scala, это не может быть и речи. – midor

2

у вас может быть «реестр» Router проб и их соответствующих типов сообщений.

class CompositeRouter implements Router { 

    private Map<Class,Router> registry = new HashMap<>() 

    void registerRouter(Class<? extends MessageBase> messageClass, Router router) { 
    register.put(messageClass, router); 
    } 

    @Override 
    void process(MessageBase message) { 
    // here you can implement more sophisticated logic 
    // to find most appropriate Router for given message according 
    // type hierarchy 
    Router router = registry.get(message.getClass()); 
    router.process(message); 
    } 
} 
4

Вот как я обычно решал проблемы, как это в прошлом:

Во-первых, в интерфейсе Router, так как кажется, вы собираетесь наиболее Router реализации, за исключением Композит обрабатывать только один тип сообщения, изменить определение интерфейса на нечто похожее на:

interface Router<T extends MessageBase> { 
    void route(T message); 

} 

Это устраняет необходимость предоставления интерфейсов для различных Router s, что Handl конкретные реализации. Производный Router классы становятся что-то вроде:

class OtherDerivedRouter implements Router<OtherDerivedMessage> { 

    @Override 
    void route(OtherDerivedMessage message) { //... }; 

} 

Так теперь, что происходит в CompositeRouter? Ну, мы делаем что-то вроде этого:

class CompositeRouter implements Router<MessageBase> { 

    protected static class RouterAdaptor< T extends MessageBase> implements Router<MessageBase> { 

     private Router<T> router; 
     private Class<T> klass; 

     RouterAdaptor(Router<T> router, Class<T> klass) { 
      this.router = router; 
      this.klass = klass; 
     } 

     @Override 
     public void route(MessageBase message) { 
      try { 
       router.route(klass.cast(message)); 
      } (catch ClassCastException e) { 
       // Do whatever, something's gone wrong if this happens 
      } 
     } 
    } 

    private Map<Class<?>, RouterAdaptor<?>> routerMap; 

    @Override 
    public void route(MessageBase message) { 
     RouterAdaptor<?> adaptor = routerMap.get(message.getClass()); 
     if (adaptor != null) { 
      adaptor.route(message) 
     } else { 
      // do your default routing case here 
     } 
    } 

    public <T extends MessageBase> void registerRouter(Router<T> router, Class<T> klass) { 
     // Right now don't check for overwrite of existing registration, could do so here 
     routerMap.put(klass, new RouterAdaptor<T>(router, kass)); 
    } 

    CompositeRouter(/*...*/) { 
     //initialize routerMap with Map type of choice, etc 
    } 

} 

RouterAdaptor делает тяжелую работу диспетчерских правильный тип сообщения, ожидаемый Router реализации он держит. И уходит CompositeRouter, которому необходимо только сохранить реестр этих адаптеров для их типа сообщения.

Самым большим недостатком этого подхода является то, что благодаря Type Erasure невозможно создать реализацию Router, которая обрабатывает непосредственно несколько типов сообщений. С точки зрения Java, во время выполнения Router<MessageBase> совпадает с Router<OtherDerivedMessage>, и поэтому незаконно иметь что-то вроде SuperRouter implements Router<MessageBase>, Router<OtherDerivedMessage>, в отличие от шаблонов C++. Вот почему вам нужно пройти explisit Class<T> объектов вокруг, а не просто сделать вывод о том, что это тип непосредственно из Router<T>.

Смежные вопросы