Java的.用于实现侦听器的正确模式

Jak*_*ake 25 java design-patterns listeners

通常我会遇到一个给定对象需要有很多监听器的情况.例如,我可能有

class Elephant {
  public void addListener( ElephantListener listener ) { ... }
}
Run Code Online (Sandbox Code Playgroud)

但我会遇到很多这样的情况.也就是说,我也会有一个Tiger对象TigerListener.现在,TigerListeners和ElephantListeners完全不同:

interface TigerListener {
  void listenForGrowl( Growl qrowl );
  void listenForMeow( Meow meow );
}
Run Code Online (Sandbox Code Playgroud)

interface ElephantListener {
  void listenForStomp( String location, double intensity );
}
Run Code Online (Sandbox Code Playgroud)

我发现我总是必须在每个动物类中继续重新实现广播机制,并且实现总是相同的.有首选模式吗?

mat*_*t b 28

Listener您可以发送它,而不是每个事件类型都有特定的方法,更改接口以接受泛型Event类.然后Event,如果需要,可以将子类化为特定的子类型,或者使其包含诸如的状态double intensity.

然后TigerListener和ElephentListener成为

interface TigerListener {
    void listen(Event event);
}
Run Code Online (Sandbox Code Playgroud)

实际上,您可以进一步将此接口重构为简单Listener:

interface Listener {
    void listen(Event event);
}
Run Code Online (Sandbox Code Playgroud)

Listener然后,您的实现可以包含他们关心的特定事件所需的逻辑

class TigerListener implements Listener {
    @Overrides
    void listen(Event event) {
        if (event instanceof GrowlEvent) {
            //handle growl...
        }
        else if (event instance of MeowEvent) {
            //handle meow
        }
        //we don't care about any other types of Events
    }
}

class ElephentListener {
    @Overrides
    void listen(Event event) {
        if (event instanceof StompEvent) {
            StompEvent stomp = (StompEvent) event;
            if ("north".equals(stomp.getLocation()) && stomp.getDistance() > 10) { 
                ... 
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

订阅者和发布者之间的关键关系是发布者可以向订阅者发送事件,不一定是它可以向其发送某些类型的事件 - 这种类型的重构将该逻辑从接口推送到特定的实现中.


Sur*_*gch 13

对于那些来这里只想做一个听众的人来说,这是一个更普遍的答案.我总结了从CodePath 创建自定义侦听器.如果您需要更多解释,请阅读该文章.

这是步骤.

1.定义接口

这是在需要与某个未知父级进行通信的子类中.

public class MyClass {

    // interface
    public interface MyClassListener {
        // add whatever methods you need here
        public void onSomeEvent(String title);
    }
}
Run Code Online (Sandbox Code Playgroud)

2.创建一个监听器设置器

将私有侦听器成员变量和公共setter方法添加到子类.

public class MyClass {

    // add a private listener variable
    private MyClassListener mListener = null;

    // provide a way for another class to set the listener
    public void setMyClassListener(MyClassListener listener) {
        this.mListener = listener;
    }


    // interface from Step 1
    public interface MyClassListener {
        public void onSomeEvent(String title);
    }
}
Run Code Online (Sandbox Code Playgroud)

3.触发监听器事件

子对象现在可以在侦听器接口上调用方法.一定要检查null,因为可能没有人在听.(也就是说,父类可能没有为我们的侦听器调用setter方法.)

public class MyClass {

    public void someMethod() {
        // ...

        // use the listener in your code to fire some event
        if (mListener != null) 
            mListener.onSomeEvent("hello");
    }


    // items from Steps 1 and 2

    private MyClassListener mListener = null;

    public void setMyClassListener(MyClassListener listener) {
        this.mListener = listener;
    }

    public interface MyClassListener {
        public void onSomeEvent(String myString);
    }
}
Run Code Online (Sandbox Code Playgroud)

4.在Parent中实现监听器回调

父级现在可以使用我们在子类中设置的侦听器.

例1

public class MyParentClass {

    private void someMethod() {

        MyClass object = new MyClass();
        object.setMyClassListener(new MyClass.MyClassListener() {
            @Override
            public void onSomeEvent(String myString) {
                // handle event
            }
        });
    }
}
Run Code Online (Sandbox Code Playgroud)

例2

public class MyParentClass implements MyClass.MyClassListener {

    public MyParentClass() {
        MyClass object = new MyClass();
        object.setMyClassListener(this);
    }

    @Override
    public void onSomeEvent(String myString) {
        // handle event
    }
}
Run Code Online (Sandbox Code Playgroud)