문제

맞춤형 Java 이벤트를 해고하는 Java 클래스가 있습니다. 코드의 구조는 다음과 같습니다.

public class AEvent extends EventObject {
...
}

public interface AListener extends EventListener {

  public void event1(AEvent event);

}

public class A {

  public synchronized void addAListener(AListener l) {
  ..
  }

  public synchronized void removeAListener(AListener l) {
  ..
  }

  protected void fireAListenerEvent1(AEvent event) {
  ..
  }
}

모든 것이 올바르게 작동하지만 새로운 이벤트를 시작할 수있는 A (B)의 새로운 서브 클래스를 만들고 싶습니다. 다음 수정을 생각하고 있습니다.

public class BEvent extends AEvent {
...
}

public interface BListener extends AListener {

  public void event2(BEvent event);
}

public class B extends A {

  public synchronized void addBListener(BListener l) {
  ..
  }

  public synchronized void removeBListener(BListener l) {
  ..
  }

  protected void fireBListenerEvent2(AEvent event) {
  ..
  }

}

이것이 올바른 접근법입니까? 나는 예제를 찾기 위해 웹을 검색하고 있었지만 아무것도 찾을 수 없었습니다.

이 솔루션에는 내가 좋아하지 않는 몇 가지가 있습니다.

  1. BListener 두 가지 방법이 사용됩니다 AEvent 다른 사람은 사용합니다 BEvent 매개 변수로.
  2. B 수업 모두가 있습니다 addAListener 그리고 addBListener 행동 양식. 개인 키워드로 Addalistener를 숨겨야합니까? 업데이트 : 개인 키워드로 숨길 수 없습니다
  3. 비슷한 문제 fireAListenerEvent1 그리고 fireBListenerEvent1 행동 양식.

Java 버전 1.5를 사용하고 있습니다.

도움이 되었습니까?

해결책

이유가 없습니다 BListener 확장해야합니다 AListener.

당신은 정말로 관심있는 모든 사람을 강요하고 싶습니까? B 또한 구현할 이벤트 event1()?

또한 추가 할 수 없습니다 addAListener(), 파생 클래스는 상위 클래스에 존재하는 메소드의 가시성을 줄일 수 없기 때문에. 또한, 당신은 할 필요가 없거나 위반할 것입니다. Liskov 대체 원리 (모든 B는 A가 할 수있는 모든 것을 할 수 있어야합니다).

그리고 마지막 말로, 나는 그것을 만들 것이다 fire*() 방법 보호. 일반적으로 공개적으로 유지하고 공개 회원의 수를 줄이면 공개 인터페이스를 깨끗하게 유지할 이유가 전혀 없습니다.

다른 팁

내유를 사용하지 말고, 당신이 원하는 것이 아니며 부서지기 쉬우 며 디자인을 바꾸기가 어렵습니다. 구성은 더 유연하고 디자인에 더 나은 접근 방식입니다. 이벤트가 변경되지 않아야하기 때문에 항상 인터페이스를 최대한 세분화하십시오. 그들은 나머지 시스템과의 계약입니다. 새로운 기능을 추가 해야하는 경우 첫 번째 옵션은 이벤트에 더 많은 정보를 추가하는 것입니다. 그것이 적절하지 않은 경우 해당 이벤트를 제공하기위한 새로운 인터페이스를 설계해야합니다. 이로 인해 영향을받지 않는 기존 코드를 변경해야합니다.

여기에 내가 가장 좋아하는 패턴이 있습니다. 일반적으로 관찰자라고합니다.

해당 이벤트 유형에 대한 메소드를 정의하는 새 인터페이스를 만듭니다 (fooevent () addfooeventListener () removeFooeventListener ()). 이러한 이벤트를 생성하는 콘크리트 클래스 에서이 인터페이스를 구현하십시오. (나는 보통 이것을 sourcesfooevent, firesfooevent, fooeventsource 등과 같은 것과 같은 것으로 부릅니다.

코드 복제를 줄이려면 청취자의 등록을 처리하는 도우미 클래스를 구성하고 컬렉션에 저장하며 이벤트를 게시하기위한 화재 방법을 제공 할 수 있습니다.

제네릭은 여기서 도움이 될 수 있습니다. 먼저 일반적인 리스너 인터페이스 :

public interface Listener<T> {
  void event(T event);
}

다음으로 일치하는 EventSource 인터페이스 :

public interface EventSource<T> {
    void addListener(Listener<T> listener);
}

마지막으로 청취자 등록 및 이벤트 파견을 처리하기 위해 도우미 클래스를 신속하게 구축하는 추상 기본 클래스 :

public abstract class EventDispatcher<T> {
    private List<Listener<T>> listeners = new CopyOnWriteArrayList<T>();

    void addListener(Listener<T> listener) {
      listeners.add(listener);
    }    

    void removeListener(Listener<T> listener) {
      listeners.remove(listener);
    }

    void fireEvent(T event) {
      for (Listener<T> listener : listeners) {
        listener.event(event);
      } 
    }
}

캡슐화를 통해 추상 EventSpatcher를 사용하므로 다른 클래스는 특정 클래스를 확장 할 필요가없는 동시에 EventSource를 쉽게 구현할 수 있습니다.

public class Message {
}

public class InBox implements EventSource<Message> {

  private final EventDispatcher<Message> dispatcher = new EventDispatcher<Message>();

  public void addListener(Listener<Message> listener) {
    dispatcher.addListener(listener);
  }

  public void removeListener(Listener<Message> listener) {
    dispatcher.removeListener(listener);
  }

  public pollForMail() {
    // check for new messages here...
    // pretend we get a new message...

    dispatcher.fireEvent(newMessage);
  }
}

바라건대 이것은 유형 안전 (중요), 유연성 및 코드 재사용 사이의 균형을 보여줍니다.

Saua에 대한 귀하의 의견에서 B 발사 B가 자동으로 해고 될 것이라는 것을 이해합니다.

단일 유형의 리스너를 사용한 다음 상속, 위임 및 제네릭을 혼합하지 않겠습니까?

class AEvent {}
class BEvent extends Event{}

interface EventListner<E extends AEvent>
{
   onEvent(E e);
}

class ListenerManager<E extends AEvent>{
    addListner(EventListener<? extends E>){}
    removeListner(EventListener<? extends E>){}
    fire(E e);
}

class A extends ListenerManager<AEvent>
{
}

class B extends ListenerManager<BEvent>
{
   A delegatorA;

  @Override addListener(EventListener<? extends BEvent> l)
  {
    super.addListner(l);
    delegatorA.addListener(l);
  }       

  @Override removeListener(EventListener<? extends BEvent> l)
  {
    super.removeListner(l);
    delegatorA.removeListener(l);
  }       

  @Override fire(BEvent b)
  {
    super.fire(b);
    a.fire(b)
  }

}

설명 : 청취자 관리 코드는 기본 클래스 리스너 관리자에서 공유됩니다. B는 제네릭 컴파일 시간 점검으로 인해 블리너 만받을 수 있습니다. B 발사 B는 자동으로 A.

당신이 물건을 매우 간단하게 유지할 수있는 것 같습니다.

내 이해

  • 기본 수업이 있습니다 그것은 일부를 수행합니다 기본 동작

  • 보다 구체적인 서브 클래스가있을 수 있습니다 또한 더 많은 성과를 거둘 수 있습니다 특정 수술

이 경우 두 이벤트를 모두 처리해야합니다 ( 기초적인 A와 기초적인 + 특정한 b)

글쎄, 당신은 그렇게하기 위해 방법을 과부하 할 필요가 없습니다. 당신이해야 할 유일한 일은 특정 이벤트에 대한 특정 핸들러 (또는 청취자)를 추가하는 것입니다.

이벤트가 "기본"인 경우 일 수도 있습니다. 괜찮습니다.

그러나 이벤트가 구체적이면 그에 따라 반응해야합니다. 그래서 내가 할 일은 수표를 추가하는 것입니다. 안에 구별 할 특정 청취자 특정한 다음과 같은 이벤트 :

        if( whichEvent instanceof SpecificEvent ) { 
            SpecificEvent s = ( SpecificEvent ) whichEvent;
            // Do something specific here...
        }

그리고 그게 다야.

문제에 대한 귀하의 설명은 너무 추상적이므로 콘크리트 솔루션을 제안 할 수 없습니다. 그러나 달성하고자하는 것을 설명하기가 어렵다면 아마도 문제가 무엇인지 다시 분석해야 할 것입니다.

위의 이해가 정확하다면 기초적인 + 특정한 때로는) 아래의 긴 코드가 도움이 될 수 있습니다.

친애하는


import java.util.*;
class A { 

    // All the listener will be kept here. No matter if basic or specific.
    private List<Listener> listeners = new ArrayList<Listener>();


    public void add( Listener listener ) { 
        listeners.add( listener );
    }
    public void remove( Listener listener ) { 
        listeners.remove( listener );
    }


    // In normal work, this class just perform a basic operation.
    public  void normalWork(){
        performBasicOperation();
    }

    // Firing is just firing. The creation work and the 
    // operation should go elsewhere.
    public void fireEvent( Event e ) { 
        for( Listener l : listeners ) { 
            l.eventHappened( e );
        }
    }

    // A basic operation creates a basic event
    public void performBasicOperation() { 
        Event e = new BasicEvent();
        fireEvent( e );
    }
}

// Specialized version of A.
// It may perform some basic operation, but also under some special circumstances
// it may  perform an specific operation too
class B extends A { 

    // This is a new functionality added by this class.
    // Hence an specifi event is fired.
    public  void performSpecificOperation() {
        Event e = new SpecificEvent();
        // No need to fire in different way
        // an event is an event and that's it.
        fireEvent( e );
    }

    // If planets are aligned, I will perform 
    // an specific operation.
    public  void normalWork(){
        if( planetsAreAligned() ) { 
            performSpecificOperation();
        } else { 
            performBasicOperation();
        }
    }
    private boolean planetsAreAligned() { 
        //return new Random().nextInt() % 3 == 0;
        return true;
    }
}

// What's an event? Something from where you can get event info?
interface Event{
    public Object getEventInfo();
}

// This is the basic event.
class BasicEvent implements Event{
    public Object getEventInfo() {
        // Too basic I guess.
        return "\"Doh\"";
    }
}
// This is an specific event. In this case, an SpecificEvent IS-A BasicEvent.
// So , the event info is the same as its parent. "Doh".
// But, since this is an SpecificEvent, it also has some "Specific" features.
class SpecificEvent extends  BasicEvent {

    // This method is something more specific.
    // There is no need to overload or create 
    // different interfaces. Just add the new  specific stuff
    public Object otherMethod() {
        return "\"All I can say is , this was an specific event\"";
    }
}

// Hey something just happened.
interface Listener { 
    public void eventHappened( Event whichEvent );
}

// The basic listner gets information 
// from the basic event. 
class BasicEventListener implements Listener { 
    public void eventHappened( Event e ) {
            System.out.println(this.getClass().getSimpleName() + ": getting basic functionality: " + e.getEventInfo());
        }
}


// But the specific listner may handle both.
// basic and specific events.
class SpecificListener extends BasicEventListener { 
    public void eventHappened( Event whichEvent ) {
        // Let the base to his work
        super.eventHappened( whichEvent );


        //  ONLY if the event if of interest to THIS object
        // it will perform something extra ( that's why it is specific )
        if( whichEvent instanceof SpecificEvent ) { 
            SpecificEvent s = ( SpecificEvent ) whichEvent;
            System.out.println(this.getClass().getSimpleName() + ": aaand  getting specific functionality too: " + s.otherMethod() );
            // do something specific with s 
        }
    }
}

// See it run. 
// Swap from new A() to new B() and see what happens.
class Client { 
    public static void main( String [] args ) { 
        A a = new B();
        //A a = new A();

        a.add( new BasicEventListener() );
        a.add( new SpecificListener() );

        a.normalWork();
    }
}

샘플 출력 :

BasicEventListener: getting basic functionality: "Doh"
SpecificListener: getting basic functionality: "Doh"
SpecificListener: aaand  getting specific functionality too: "All I can say is , this was an specific event"

더 가져 가면 인터페이스를 제거하여 더 간단하게 유지할 수도 있습니다.

만약에

public class BEvent extends AEvent {
...
}

public interface BListener extends AListener {

  public void event2(BEvent event);
}

당신은 다음과 같이 할 수 없습니다.

public class B extends A {

  @Override
  public synchronized void addAListener(AListener l) {
    if (l instanceof BListener) {
       ...
    } else {
       super.addAListener(l);
    }
  }
  ...
}

내가 의견에서 말했듯이, 나는 당신이 실제로 무엇을 달성하고 싶은지 잘 모르겠습니다. 누가 어디에서 전화를 받고, 전화를받을 때 무엇을해야합니까?

우리의 관계에 대해 우리가 가진 정보가 거의없는 것을 기반으로 A & B, 나는 그것이 혼란 스럽다고 생각합니다 BListener 의 하위 인터페이스 AListener. 이름에서 알 수 있듯이 a BListener 듣는 것이 좋습니다 BEventS, 이미 서브 클래스 AEvent에스. 명확성을 위해, 청취자는 분별력있는 목적을 가져야합니다. 불필요하게 겹쳐서는 안됩니다. 게다가, 수업에서 이미 별도의 방법을 정의하기 때문에 그러한 중첩 청취자가 필요하지 않습니다. B 다양한 유형의 청취자를 처리합니다.

내 요점을 설명하려면 코드 다음에 스타일이있는이 예를 고려하십시오.

public class MovableMouseEvent extends EventObject

public class ClickableMouseEvent extends MovableMouseEvent

public interface MovableMouseListener extends EventListener
  // mouseMoved(MovableMouseEvent)

public interface ClickableMouseListener extends MovableMouseListener 
  // mouseClicked(ClickableMouseEvent) 

public class MovableMouseWidget
  // {addMovableMouseListener,removeMovableMouseListener}(MovableMouseListener)
  // fireMovableMouseEvent(MovableMouseEvent)                           

public class ClickableMouseWidget extends MovableMouseWidget
  // {addClickableMouseListener,removeClickableMouseListener}(ClickableMouseListener)
  // fireClickableMouseEvent(ClickableMouseEvent)                                      

이 디자인은 작동하지만 혼란 스럽습니다 ClickableMouseListener 두 종류의 이벤트를 처리합니다 ClickableMouseWidget 당신이 올바르게 지적한 것처럼 두 종류의 청취자를 처리합니다. 이제 상속 대신 구성을 사용하는 다음 대안을 고려하십시오.

public class MouseMoveEvent extends EventObject // note the name change

public class MouseClickEvent extends EventObject // don't extend MouseMoveEvent 

public interface MouseMoveListener extends EventListener
  // mouseMoved(MouseMoveEvent)

public interface MouseClickListener extends EventListener // don't extend MouseMoveListener 
  // mouseClicked(MouseClickEvent) 

public interface MouseMoveObserver
  // {addMouseMoveListener,removeMouseMoveListener}(MouseMoveListener)
  // fireMouseMoveEvent(MouseMoveEvent)

public interface MouseClickObserver
  // {addMouseClickListener,removeMouseClickListener}(MouseClickListener)
  // fireMouseClickEvent(MouseClickEvent)

public class MovableMouseWidget implements MouseMoveObserver

public class ClickableMouseWidget implements MouseMoveObserver, MouseClickObserver
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top