2010-07-01 4 views
2

Я рефакторинг существующего государственного аппарата, который сильно использовал аргументы case и emums. Я разбил его на объекты событий и событий, каждый из которых соответствует конечной машине. EventHandlers возвращают новые события для распространения через конечный автомат.Как передать экземпляр подтипа класса соответствующему перегруженному методу?

Итак, мой код выглядит примерно так:

public class Event { 
    //common fields and methods 
} 

public class SpecificEvent extends Event { 
    //fields specific to this event 
} 

public class AnotherEvent extends Event { 
    //fields specific to this event 
} 

public class EventHandler { 
    public Event handleEvent(SpecificEvent evt) { 
     //do default something 
    } 
    public Event handleEvent(AnotherEvent evt) { 
     //do default something else 
    } 
} 

public class StateOneEventHandler extends EventHandler { 
    public Event handleEvent(SpecificEvent evt) { 
     //do State1 things 
    } 
    public Event handleEvent(AnotherEvent evt) { 
     //do other State1 things 
    } 
} 

public class StateTwoEventHandler extends EventHandler { 
    public Event handleEvent(SpecificEvent evt) { 
     //do State2 things 
    } 
    public Event handleEvent(AnotherEvent evt) { 
     //do other State2 things 
    } 
} 

Моя проблема и вопрос: я лишь вскользь общие ссылки событий вокруг в моей государственной машине, так как я могу назвать правильный обработчик мероприятие?

Event evt = new SpecificEvent(); 
EventHandler handler = new StateOneEventHandler(); 

//... later 

handler.handleEvent(evt); //compiler error 

Каков наилучший способ выполнения этого мероприятия «отправка»?

ответ

4

Вы правы, что перегруженные методы не смогут это решить (поскольку метод для вызова определяется во время компиляции).

Я хотел бы предложить, что вы должны были бы реорганизовать это, либо добавить метод к event который принимает обработчик в качестве параметра (что-то вроде public void act(EventHandler handler)), или же переписать обработчик так, чтобы она не необходимости, чтобы быть в курсе типа события. Если Event является разумным интерфейсом/суперклассом, он будет выставлять достаточно функциональности, так что EventHandler не нуждается в, чтобы узнать о конкретном типе.

Вы можете, конечно, всегда снимать, если вам действительно нужно, но в целом вы должны следовать Закону Деметра и взаимодействовать только с объектами событий через интерфейс Event.

+0

Мне нравится добавлять метод act() к событию. Моя цель заключалась в том, чтобы уменьшить код шаблона, но я предпочел бы разместить его в более или менее фиксированном количестве событий (около 15 пока), чем в каждом обработчике событий (около 60 пока). Благодаря! – Mark

1

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

Возможно, вы захотите взглянуть на шаблон посетителя, который, по-видимому, подходит вашему прецеденту: http://en.wikipedia.org/wiki/Visitor_pattern для вдохновения для организации иерархии классов, чтобы не требовалось никаких бросков.

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

0

Как о наличии другого метода в EventHandler:

public Event handleEvent(Event evt) { 
    if (evt instanceof SpecificEvent) { 
     return handleEvent((SpecificEvent)evt); 
    } 
    if (evt instanceof AnotherEvent) { 
     return handleEvent((AnotherEvent)evt); 
    } 
    // code for unknown type 
} 

Если у вас есть переменная типа Event это будет называться и будет пытаться вызвать один из других определенных методов. Если это расширение типа, которое не имеет определенного метода, оно содержит код для этого случая. В классах, расширяющих EventHandler, будут вызываться перезаписанные методы.

Я знаю, что это не совсем красиво, но оно должно работать.

Edit:

Вы также можете попробовать сделать это рефлекторно:

public Event handleEvent(Event evt) throws InvocationTargetException{ 
    try { 
     Method m = this.getClass().getMethod("handleEvent", evt.getClass()); 
     return (Event) m.invoke(this, evt); 
    } catch (NoSuchMethodException nsme) { 
     nsme.printStackTrace(); 
    } catch (IllegalAccessException iae) { 
     iae.printStackTrace(); 
    } catch (InvocationTargetException ite) { 
     ite.getCause().printStackTrace(); 
     throw ite; 
    } 
    // code for unknown type 
} 

Это будет работать лучше, если у вас есть много типов событий. Единственная проблема заключается в том, что теперь вы должны заботиться об исключениях.Я предполагаю, что если произошли NoSuchMethodException или IllegalAccessException, вы можете их игнорировать, так как это означает, что для этого события (или его недоступности) не задан какой-либо метод, поэтому вам нужно вернуться к обработке по умолчанию неизвестных типов. Возможно, вы не захотите игнорировать InvocationTargetException, поскольку это означает, что метод был вызван, но сам метод вызвал исключение, поэтому это означает, что существует проблема с кодом в методе.

+0

Ick .... это то, от чего я ушел от гигантских операторов switch. Я сделаю это, если не смогу придумать что-нибудь еще, но я бы предпочел. – Mark

+0

Я действительно делал это с отражением, прежде чем я сделал это сообщение, но хотел уйти от него по соображениям производительности. Это то, что я впервые придумал. – Mark

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