【设计模式】-11监听者模式

目录

 

1.什么是监听者模式

2.监听者模式的组成(成员)

3.代码实现

4.思考


1.什么是监听者模式

监听者模式在现实中无处不在,举个常见的例子,我们经常在电影片段中看到,当信号侦察兵接收到上级下发的某个指令信号,会立即执行相应动作.在古代,亦有烽火台,当烽火台被点燃时,远处哨兵观察到烟雾后会立刻通知士兵开始防御外敌... 类似这种收到某个"信号"后立即做出相应反馈的,在编程领域称之为监听者模式,这么说比较笼统,可能比较容易与观察者模式混淆,下面详细介绍下监听者模式.

2.监听者模式的组成(成员)

监听者模式由事件源,事件对象,事件监听器三者共同构成.

事件源:被监听的事件本身,也就是可以触发监听器的某件事件,对应上面例子里的烽火台被点燃这个事件.

事件对象:事件对象里面存放了对事件源的引用,可以通过事件对象来获取事件源,是对事件源的包装.

事件监听器:定义事件发生后的动作,比如烽火台被点然后,要立即通知士兵防御.监听器里的具体逻辑由开发来实现.

3.代码实现

单看上面的介绍可能比较空洞,甚至难以理解,作为程序猿还是代码来得更直接.

先上一个低配版:

场景:以我喜欢的一款游戏吃鸡为例,我特别喜欢舔空投,几乎每把都是空投之王,我喜欢顶配的感觉,当然也经常为此送命...

游戏中我一旦看到有人放信号枪,就会过去舔空投,下面用代码来实现下...

事件源:

public class SignalSource {
    private SignalListener signalListener;

    public void signal(){
        System.out.println("打响了信号枪...");
        if (signalListener!=null){
            signalListener.doGetAirDrop(new Signal(this));
        }
    }

    public SignalSource(SignalListener signalListener) {
        this.signalListener = signalListener;
    }
}

事件对象:

public class Signal {
    private SignalSource signalSource;

    public SignalSource getSignalSource() {
        return signalSource;
    }

    public void setSignalSource(SignalSource signalSource) {
        this.signalSource = signalSource;
    }

    public Signal(SignalSource signalSource) {
        this.signalSource = signalSource;
    }
}

监听器:

public interface SignalListener {
    void doGetAirDrop(Signal signal);
}

测试:

public class Client {
    public static void main(String[] args) {
        SignalSource signalSource = new SignalSource((s)->{
            System.out.println("看到有人在打信号枪,我要去舔空投了...");
        });
        signalSource.signal();
    }
}

结果:

可以看出,当事件源被触发后,监听器里的逻辑被执行了. 也就是打响了信号枪之后,我开始去舔空投了.以上便是监听者模式的简单实现,看到这里,你是否跟我有同样的疑问,且继续往下看.

4.思考

为什么要存在一个Signal(事件对象)?直接在事件源里触发监听器里的逻辑不就好了吗?要它有卵子用???

假如我把Signal(事件对象)去掉后,这特么不就变成了观察者模式了??? 

存在必有意义,既然23种设计模式里把观察者模式和监听者模式归为不同的设计模式,那必定有它的原因.

为了解决这个疑问,不妨先思考下,如果事件源不是打响信号枪,而是有人向投掷了一枚手雷呢?那此时要执行的动作应该是迅速卧倒或躲避,而非去舔空投. 在实际业务中,需要监听的事件源往往是多种多样的,针对不同的事件源需要有不同的监听器来执行后续操作,此时如果没有事件对象,就会像观察者模式一样,一旦事件源触发,所有监听事件都会被触发,这显然不合理,别人放个信号枪我还要卧倒或者躲避???

上面的代码似乎不能很好的体现这一点,但可以帮我们更好的理解监听者模式,天下大事,必做于易.

下面我写一个比较规范的监听者模式,继承自JDK的标准库的实现,在实际开发中和Spring源码中多采用这种方式:

同样的我还以上面的场景为例,分别模拟监听信号枪和手雷等事件:

事件源:

public class EventSource {
    private List<Listener> listenerList = new ArrayList<>();

    public void fireEvent(EventObject event) {
        listenerList.parallelStream()
            .distinct()
            .collect(Collectors.toList())
            .forEach(l -> l.invoke(event));
    }

    public void addListener(Listener listener) {
        if (listener != null) {
            listenerList.add(listener);
        }
    }

    public void removeListener(Listener listener) {
        if (listener != null) {
            listenerList.remove(listener);
        }
    }
}

事件对象:

//继承自Java.util.EventObject
public class Event extends EventObject {
    private Object source;
    private Integer state;

    public Event(Object source) {
        super(source);
    }

    @Override
    public Object getSource() {
        return source;
    }

    public void setSource(Object source) {
        this.source = source;
    }

    public Integer getState() {
        return state;
    }

    public void setState(Integer state) {
        this.state = state;
    }
}

监听器:

//继承自java.util.EventListener
public interface Listener extends EventListener {
    void invoke(EventObject event);
}

监听器实现类:

//针对打信号枪事件的监听
public class SignalListener implements Listener {
    @Override
    public void invoke(EventObject eventObject) {
        Event event = (Event)eventObject;
        if (Objects.equals(event.getState(),StateEnum.SIGNAL.getState())){
            System.out.println("有人打信号枪了,我要去舔空投...");
        }
    }
}
//针对扔手雷事件的监听
public class BoomListener implements Listener {
    @Override
    public void invoke(EventObject eventObject) {
      Event event = (Event)eventObject;
      if (Objects.equals(event.getState(),StateEnum.BOOM.getState())){
          System.out.println("有人扔手雷了,我要卧倒或者躲避...");
      }
    }
}

状态管理枚举类:

public enum StateEnum {
    SIGNAL(1),
    BOOM(2)
    ;
    private Integer state;

    StateEnum(Integer state) {
        this.state = state;
    }

    public Integer getState() {
        return state;
    }

    public void setState(Integer state) {
        this.state = state;
    }
}

测试类:

public class Client {
    public static void main(String[] args) {
        EventSource eventSource = new EventSource();
        SignalListener signalListener = new SignalListener();
        BoomListener boomListener = new BoomListener();
        eventSource.addListener(signalListener);
        eventSource.addListener(boomListener);
        Event event = new Event(eventSource);
        event.setState(StateEnum.SIGNAL.getState());
        eventSource.fireEvent(event);
        event.setState(StateEnum.BOOM.getState());
        eventSource.fireEvent(event);
    }
}

效果:

可以通过具体的事件状态来实现对指定事件的监听,也可以通过事件源的addListener和removeListener来实现对监听器的热拔插,做到我想监听什么事件,就监听什么事件,不想监听的事件就不监听,由此也可以看出来,监听器模式似乎在功能上比观察者模式更灵活,但实现难度和复杂度要高于观察者模式,总之各有适用场景,灵活运用即可.

如果看完这篇你仍对监听者模式和观察者模式不够清楚,也不能很好的对应场景下使用对应的设计模式,没关系,可以参考我在下篇中详解监听者模式和观察者模式.

  • 8
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值