2009-06-30 2 views
1

시나리오 :Java - 일반 ChangeListener

나는 모든 컨테이너가 MyContainedObject 클래스에서 상속 된 컨테이너 오브젝트를 보유하고 있습니다. 컨테이너 클래스의 소비자는 포함 된 객체에 직접 액세스 할 수는 없지만 객체가 언제 변경되는지 알고 싶어합니다.

디자인 결정 :

특정 클래스 형식에의 ChangeEvent를 수신 할 수있는 가장 좋은 방법은 무엇입니까? 나의 초기 생각은 제네릭으로 뭔가를하는 것이다. 예 :

private TreeMap<Class, ChangeListener> listeners; 

public <T extends MyContainedObject> addChangeListenerForObjectsOfType(Class<T> className, ChangeListener listener) 
{ 
    listeners.put(className, listener); 
} 

변경 사항이 감지되면 컨테이너 클래스는 목록을 반복하여 해당 클래스 유형에 대해 등록 된 리스너에만 알립니다.

기타 제안 사항?

감사합니다.

답변

5

TreeMap의 키 유형이 MyContainedObject가 아닌 Class 인 것으로 가정합니다.

실제로 특정 클래스 유형에서 ChangeEvents를 수신해야하는 경우 청취자를 설정 한 후에도 요소를 컬렉션에 추가 할 수있게하려면이 방법은 상당히 합리적입니다. 아마도 동일한 유형의 리스너를 여러 개 지원해야 할 것이므로 Multimap 클래스 (Google Collections 일부)를 사용하거나지도의 값에 컬렉션 (아마도 IdentityHashSet)을 사용해야합니다.

ChangeListener에 유형 매개 변수를 추가하여 이벤트가 이미 적절한 유형으로 형 변환 된 객체를 가져올 수 있도록 할 수도 있습니다.

interface ChangeListener<T> { 
    void changed(T obj, /* whatever */); 
} 

당신은이 작업을 위해 컨테이너의 내부 검사되지 않은 캐스팅을해야 할 것이다, 그러나 긴 리스너가 추가로 방법은 옳은 일을 수행으로 안전해야한다. 예 :

public <T extends MyContainedObject> addChangeListener(Class<T> klass, 
                 ChangeListener<? super T> listener) { 
    ... 
}  

private <T extends MyContainedObject> Set<ChangeListener<? super T>> getChangeListeners(T obj) { 
    Set<ChangeListener<? super T>> result = new IdentityHashSet<ChangeListener<? super T>>(); 
    for (Map.Entry<Class<? extends MyContainedObject>, Set<ChangeListener<?>>> entry : listeners.entrySet()) { 
     if (entry.getKey().isInstance(obj)) { 
      // safe because signature of addChangeListener guarantees type match 
      @SuppressWarnings("unchecked") 
      Set<ChangeListener<? super T>> listeners = 
       (Set<ChangeListener<? super T>>) entry.getValue(); 
      result.addAll(listeners); 
     } 
    } 
    return result; 
} 

작은 사소한 것 : Class 객체를 보유하는 변수의 이름으로 "className"을 사용하지 마십시오. 클래스 이름은 String입니다. 일반적으로 Class.getName() 등의 결과입니다. 약간 짜증나지만, 일반적으로 피할 수있는 규칙은 "class"가 예약어라는 사실을 잊어 버리는 것입니다 그것은 "klass"또는 "cls"중 하나입니다.

또한 청취자를 추가 한 후 컬렉션을 업데이트 할 필요가없는 경우 akf가 제안한 것과 같이 간단하게 처리 할 수 ​​있습니다.

+0

고맙습니다. 이것은 내가 염두에 두었던 것입니다. 또한 MyContainedObject보다는 Class를 의미한다는 점에서 옳습니다. – javacavaj

1

컨테이너 프록시에 질문에 포함 된 객체에 대한 addChangeListener 콜을 사용하기 만하면됩니다. 이를 통해 청취자 계층 구조의 다른 레벨을 추가하지 않고도 청취자 목록을 유지하고 필요에 따라 호출을 시작할 수 있습니다.

+1

이것은 좋은 제안이지만, 리스너를 추가 한 후에 포함 된 객체 세트를 수정하지 않거나 포함 된 객체 세트와 그 위에 리스너가있는 객체 세트가 괜찮은 경우에만 작동합니다 시각. –

0

이 유형별 통지 방식의 한 가지 문제점은 클라이언트가 특정 인터페이스가 변경되었을 때 관심이있는 수신기를 잠재적으로 등록 할 수 있다는 것입니다. 예 : addChangeListenerForObjectsOfType(Iterable.class). 즉, 인터페이스에서 리스너를 등록하는 것을 명시 적으로 방지하지 않는 한 알림 알고리즘이 리스너 맵에서 간단한 조회를 수행 할 수 없다는 것을 의미합니다. 대신에 더 복잡하고 덜 효율적이어야합니다.

구현 방법을 완전히 일반화하는 대신 다른 접근 방식을 택할 수도 있습니다. 훨씬 더 노골적인를 구현하는 프로그래머의 나는 개인적으로이 선호

public interface Listener { 
    void classAChanged(ChangeEvent e); 
    void classBChanged(ChangeEvent e); 
    void classCChanged(ChangeEvent e); 
} 

예를 들어, 당신은 좀 더 명시 적 리스너 인터페이스를 제공 할 수있는 당신에 관심이 최상위 하위 클래스의 소수를 식별 할 수 있다면 인터페이스를 제공하므로 코드를보다 쉽게 ​​읽을 수 있습니다. 분명히지도에 수백 가지 하위 클래스를 저장할 가능성이있는 경우에는 부적절 할 수 있습니다.

청취자 콜백 메소드에서 다운 캐스팅을 피하기 위해이 단계를 더 진행하고 일반 ChangeEvent<T extends MyObject> 구현을 제공 할 수 있습니다.

0

문제는

  1. 한 번에 하나 개의 원시 타입에 등록 할 수있는 수신기를 쓰고 싶은 청취자의 종류에 따라 발생할 수 있습니다 귀하의 제안 된 설계에 몇 가지 제한이 있습니다. 청취자가 여러 유형에 관심이 있거나 관심있는 객체를 결정할 때 사용하려는 다른 기준이있는 경우 여러 번 등록해야합니다. 또한 청취자는 어떤 유형이 있는지 알지 못할 수도 있습니다!
  2. MyContainedObject의 하위 클래스에 등록하는 경우와 하위 클래스에 하위 클래스가있는 경우를 처리하는 방법이 명확하지 않습니다.
  3. 변화는 용기가

내가 대신 컨테이너 리스너를 제안하며 인식하지 않을 경우를 MyContainedObject 일이, 각 MyContainedObject 인스턴스 또한 청취자 지원 할 수 있습니다 : 아담 스키로

public interface ContainerListener { 
    void itemAdded(MyContainedObject item); 
    void itemRemoved(MyContainedObject item); 
} 

public interface MyContainedObjectListener { 
    void itemChanged(MyContainedObject item); 
} 

을 MyConttainedObject의 제한된 수의 하위 클래스가있는 경우 다음과 같이 제안합니다.

public interface ContainerListener { 
    void circleAdded(Circle item); 
    void squareAdded(Square item); 
    void shapeRemoved(Shape item); 
} 

또는 MyContainedObjectListener에서 특정 메서드를 만들 수 있습니다. 당신이 유형에 등록하는 사용자의 요구 사항을 충족하는지 결정하는 경우

, 다음 일반 청취자하게 고려 : 여러 구현 가능성이 있기 때문에

public <T extends MyContainedObject> addChangeListenerFor(Class<T> className, ChangeListener<? super T> listener) 

어느 쪽이든을, 장점과 각 관찰자에 GoF 섹션을 읽어 단점.