2011-01-04 3 views
1

제 프로그램에는 50 가지가 넘는 이벤트가 있습니다. 내가 겪고있는 문제는 이벤트를 수신하기위한 표준 방식으로 정착 중입니다.이벤트 톤, 코드 기반 축소 방법?

제 생각에는 제네릭 인터페이스를 사용하여 청취자에게이를 구현하고 수신기를 대기열에 추가하는 것뿐이었습니다. Unfortunately this meant that classes couldn't have multiple listeners due to type erasure and multiple inheritance issues. 그래서 리스너 코드를 제거하고 다시 시작했습니다.

내 현재 (및 원래) 설정은 각 이벤트를 수신하는 인터페이스를 갖는 것입니다.

/** 
* Listener for {@link myproject.hooks.events.FingerEvent}events 
* @see myproject.hooks.events.Finger 
*/ 
public interface FingerListener extends Listener { 
     /** 
     * Invoked when an {@link myproject.hooks.events.FingerEvent}occurs 
     * @param event The generated FingerEvent 
     */ 
     public void onFinger(FingerEvent event); 
} 

그들은 모두 어떤 방법, 그 그들은 모두가 집단적으로 호출 할 수있는 단지 있도록 "청취자"를 포함하지 않는 인터페이스 Listener을 확장합니다.

문제는 이벤트 당 많은 코드가 있다는 것입니다. 당신은 이벤트와 그 getter를 가지고있다. (고맙게도 Project Lombok에 의해 간소화되었다.) 그리고 인터페이스를받는 인터페이스, 그리고 인터페이스상의 javadoc을 사용한다.

다른 문제는 각 수신기마다 다른 메서드 이름이있어서 호출 할 메서드를 찾는 매우 흥미로운 코드로 연결된다는 것입니다. 그 깨지기 쉬운, 천천히 (반사를 사용), 그리고 쓰레기처럼 보입니다. 당신이 다음 날, 믿지 않는 경우에 :

public static boolean callListener(Event event, Listener listener) {    
      //Get base name of event 
      String name = event.getClass().getSimpleName().split("Event")[0]; 

      //Try and get the correct method if it exists 
      Method listenerMethod = null; 
      try { 
        listenerMethod = listener.getClass().getMethod("on"+name, event.getClass()); 
      } catch (NoSuchMethodException ex) { 
        //Method doesn't exist, just don't call anything 
        return false; 
      } catch (SecurityException ex) { 
        throw new RuntimeException("Method on"+name+" is unaccessable", ex); 
      } 

      //Now that we have the method, attempt to execute it 
      try { 
        listenerMethod.invoke(listener, event); 
      } catch (Exception ex) { 
        throw new RuntimeException("Unexpected error when invoking method on"+name); 
      } 

      //Method executed sucessfully, return true 
      return true; 
    } 

이 자바에서 이벤트를 수신 할 수있는 표준 방법하지만 : 다음 적절한 방법을 호출하는 스파게티를 만드는 각 이벤트에 대한 리스너 인터페이스를 가지고, 또는 내가이 일을하고있다 완전히 틀렸어?

답변

2

적어도 이벤트 처리를 위임자에게 위임하는 작업을 단순화하여 반사/호출 논리를 제거 할 수 있습니다.

public interface Listener { 
    public boolean process(Event event); 
} 

과 같은 코드 변경 : 이제

public static boolean callListener(Event event, Listener listener) { 
    return listener.process(event); 
} 

을 우리가있는 경우, 리스너는 것처럼, 당신의 마커 인터페이스 하나의 방법을 추가, 그래서 그것을 처리 할 수있는 이벤트의 유형을 알고 이벤트 BreakfastDinner을 이해 리스너, 우리는 (MealListener에서) 이런 식으로 구현할 수 있습니다 :

public class MealListener implements Listener { 

    @Override 
    public boolean process(Event event) { 
    if (event instanceof Breakfast) { 
     this.onBreakfast((Breakfast) event); 
     return true; 
    } 
    if (event instanceof Dinner) { 
     this.onDinner((Dinner) event); 
     return true; 
    } 
    return false; // MealListener ignores this event 
    } 

    private void onBreakfast(Breakfast breakfastCall) { 
    // eat breakfast 
    } 

    private void onDinner(Dinner dinnerCall) { 
    // eat dinner 
    } 

} 

그런데 "많은 톤"을 두려워하지 마라. 모든 이벤트와 리스너에 대한 공통 소스 패턴을 찾고 소스 파일을 자동 생성하십시오.이 경우 개별 이벤트 및 수신기 소스 파일을 유지할 필요는 없지만 코드 생성기 및 해당 리소스 파일 (모든 이벤트 및 수신기의 기본 이름이있는 파일 기반 목록)

+0

재미있는 방법이 있습니다. MealListener는 내가 만드는 구체적인 클래스입니까? – TheLQ

+0

MealListener는 생성 한 클래스입니다. 여러 청취자가 아침과 저녁에 대한 응답으로 다른 일을하기를 원한다면 추상적 일 수 있습니다. –

+0

@Carl 사람이 여러 이벤트를 듣고 싶을 때 상황이 복잡해질 수 있습니다. 대부분이 작업을 수행했다면 이벤트와 단일 Listener 인터페이스를 제공하고 원하는 이벤트로 캐스트를 구현할 것입니다. – TheLQ

0

디자인이 잘못되었습니다. 어떤 클래스가 이벤트를 발생시키는 지간에 인터페이스에 정의 된 메소드를 호출해야합니다.

 
public interface FingerListener { 
    public void listenerCallback(FingerEvent event); 
} 

을 그리고 당신은 단순히 등록 된 각 리스너에 대한 listenerCallback 방법을 청취자를 반복하고 호출 할 수 있습니다

그래서 인터페이스는 다음과 비슷한 모습이 될 것입니다.

수신자가 다른 유형의 이벤트를 구별해야하는 경우 수신자가 검사 할 수있는 FingerEvent 클래스에 "type code"속성을 추가합니다.

+0

'listenerCallback'을 정의 할 수 있습니까? 수퍼 인터페이스'Listener' 또는 모든 Listener 클래스에 로컬로 유지할 수 있습니까? – TheLQ

+0

기본 인터페이스에서 정의해야하므로, 리스너 클래스가됩니다 (이유는 알 수 없지만). –

관련 문제