2010-04-18 2 views
1

У меня есть код, похожий на это во всех моих классах наблюдателей, которые обрабатывают события, запущенные классом шины событий. Как вы можете видеть, существует множество проверок instanceof для выбора пути действия, необходимого для надлежащего управления событиями, и мне было интересно, можно ли это сделать более чисто, исключив тесты instanceof?Чистое управление событиями

@Override 
public void handleEvent(Event event) { 
    if (event instanceof DownloadStartedEvent) { 
     DownloadStartedEvent dsEvent = (DownloadStartedEvent)event; 
     dsEvent.getDownloadCandidateItem().setState(new BusyDownloadingState()); 
    } else if (event instanceof DownloadCompletedEvent) { 
     DownloadCompletedEvent dcEvent = (DownloadCompletedEvent)event; 
     dcEvent.getDownloadCandidateItem().setState(new FinishedDownloadingState()); 
     DownloadCandidate downloadCandidate = dcEvent.getDownloadCandidateItem(). getDownloadCandidate(); 
     if (downloadCandidate.isComplete()) { 
      // start extracting 
     } 
    } else if (event instanceof DownloadFailedEvent) { 
     DownloadFailedEvent dfEvent = (DownloadFailedEvent)event; 
     dfEvent.getDownloadCandidateItem().setState(new FailedDownloadingState()); 
    } 
} 
+1

Это чистый, объектно-ориентированный способ сделать это. Другие способы будут использовать параметр события с водопада-переключателем .. почему, по-вашему, это не ясно? – Jack

+0

@Jack Это кошмар для обслуживания, если вы планируете добавить больше событий позже: всегда будет одно событие, за которое вы забыли проверить. – jqno

+0

, если у вас много событий, это будет кошмар обслуживания в любом случае до тех пор, пока вы не придадите событиям обычное поведение в классах предков, а не разделяете их. Фактически это, когда number_events> a_considerable_amount вы будете ругаться в любом случае:/ – Jack

ответ

6

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

public void handleStartedEvent(DownloadStartedEvent ev) { ... } 
public void handleCompletedEvent(DownloadCompletedEvent ev) { ... } 
public void handleFailedEvent(DownloadFailedEvent ev) { ... } 

Вы можете также рассмотреть комбинирование Завершена/Не удалось в одном случае, так это выглядит, как у вас уже есть метод IsCompleted. Вы можете обработать CompleteEvent и проверить успех. В случае успеха вы можете начать извлечение, иначе вы можете установить состояние отказа.

Моя другая мысль была бы причиной того, что вы устанавливаете новый объект в качестве индикатора состояния. Может ли быть лучше, если использовать константы/значения enum?

1

Да, если вы контролировать Event класс, и/или его подклассов. Вы можете добавить абстрактный метод handle к вашему классу Event и переместить конкретный код для каждого подкласса в этот метод.

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

abstract class Event { 
    //... 
    public abstract void handle(); 
} 

class DownloadStartedEvent extends Event { 
    //... 
    @Override 
    public void handle() { 
    getDownloadCandidateItem().setState(new BusyDownloadingState()); 
    } 
} 

// The same for the other classes 

В вашем вызывающем коде, вы просто пишете:

@Override 
public void handleEvent(Event event) { 
    event.handle(); 
} 
+0

Это будет работать, но это как-то сломает инкапсуляцию, событие не должно пытаться справиться с самим собой :) – Jack

+0

@Jack Зависит от ситуации, я думаю. Если вам это действительно не нравится, вы можете добавить параллельное дерево классов обработчиков и предоставить делегату событие. – jqno

1

Вы можете придумать более чистое решение, используя аннотации. Вам нужно определить @EventHandler и @HandlesEvent, а затем использовать его как:

@EventHandler 
public class MyEventHandler { 

    @HandlesEvent(DownloadStartedEvent.class) 
    public void handleDownloadStartedEvent(DownloadStartedEvent dse) { 
     dsEvent.getDownloadCandidateItem().setState(new BusyDownloadingState()); 
    } 

    @HandlesEvent(DownloadCompletedEvent.class) 
    public void handleDownloadCompletedEvent(DownloadComletedEvent dse) { 
     dcEvent.getDownloadCandidateItem().setState(new FinishedDownloadingState()); 
     DownloadCandidate downloadCandidate = dcEvent.getDownloadCandidateItem(). getDownloadCandidate(); 
     if (downloadCandidate.isComplete()) { 
      // start extracting 
     } 
    } 


    // etc. 

} 

Конечно вам потребуется дополнительный код для регистрации класса и его нужно будет использовать отражение, чтобы проверить, какой метод обрабатывает какое событие. Затем ваши наблюдаемые данные взаимодействуют с этим регистратором, который, в свою очередь, взаимодействует с указанным выше аннотированным классом @EventHandler.

0

если то, что вам нужно сделать, это просто экземпляр «состояний», как BusyDownloadedState или FinishedDownloadedState вы могли бы дать вашему Event класс атрибут, состояния которых является государство, которое должно быть установлено в соответствии с типом события.

Если вам необходимо иметь как простые события и сложные события, которые вы можете иметь сочетание двух вещей:

class Event 
{ 
    State state; 
    boolean isSimple; 
} 

public void handleEvent(Event event) 
{ 
    if (event.isSimple) 
    setState(event.state) 
    else 
    { 
    if (event instanceof WhateverEvent) 
     // special handling 
    } 
} 
+0

Недостатком этого является то, что если есть множество особых случаев, вы получаете более сложный вариант исходного кода. – jqno

+0

Да, вы должны выбрать гранулярность этой вещи точно. Конечно, мы недостаточно знаем о том, сколько событий или видов событий ему нужно, чтобы дать идеальное решение для его дела. Фактически в подобном случае я закончу переосмысление всего этого, как только я понимаю, что спецификация нуждается в чем-то другом. – Jack

0

Улучшение на ответ Райана, вместо того, чтобы иметь отдельный метод для каждого события, вы можете использовать дженерики , Тогда вы можете написать:

public void registerListeners() { 

eventSource.addListener(DownloadStartedEvent.class, new EventHandler<DownloadStartedEvent>() { 
    public void handleEvent(DownloadStartedEvent event) { 
     dsEvent.getDownloadCandidateItem().setState(new BusyDownloadingState()); 
    } 
}); 

eventSource.addListener(DownloadCompletedEvent.class, new EventHandler<DownloadCompletedEvent>() { 
    public void handleEvent(DownloadCompletedEvent event) { 
     dcEvent.getDownloadCandidateItem().setState(new FinishedDownloadingState()); 
     DownloadCandidate downloadCandidate = dcEvent.getDownloadCandidateItem(). getDownloadCandidate(); 
     if (downloadCandidate.isComplete()) { 
      // start extracting 
     } 
    } 
}); 

eventSource.addListener(DownloadFailedEvent.class, new EventHandler<DownloadFailedEvent>() { 
    public void handleEvent(DownloadFailedEvent event) { 
     dfEvent.getDownloadCandidateItem().setState(new FailedDownloadingState()); 
    } 
}); 
} 

Я оставляю реализацию addListener() как упражнение для читателя.

0

Как насчет:

public void handleEvent(Event e) { 
    // Not interested 
} 


public void handleEvent(DownloadStartedEvent dsEvent) { 
    dsEvent.getDownloadCandidateItem().setState(new BusyDownloadingState()); 
} 


public void handleEvent(DownloadCompletedEvent dcEvent) { 
    dcEvent.getDownloadCandidateItem().setState(new FinishedDownloadingState()); 
    DownloadCandidate downloadCandidate = dcEvent.getDownloadCandidateItem().getDownloadCandidate(); 
    if (downloadCandidate.isComplete()) { 
     // start extracting 
    } 
} 

public void handleEvent(DownloadFailedEvent dfEvent) { 
    dfEvent.getDownloadCandidateItem().setState(new FailedDownloadingState()); 
} 
Смежные вопросы