Как я могу передать экземпляр подтипа класса соответствующему перегруженному методу? - PullRequest
2 голосов
/ 01 июля 2010

Я занимаюсь рефакторингом существующего конечного автомата, в котором интенсивно используются операторы 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

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

Ответы [ 3 ]

4 голосов
/ 01 июля 2010

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

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

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

1 голос
/ 01 июля 2010

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

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

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

0 голосов
/ 01 июля 2010

Как насчет использования другого метода в 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, поскольку это означает, что метод был вызван, но сам метод вызвал исключение, поэтому это означает, что существует проблема с кодом в методе.

...