前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【设计模式】Observer 观察者模式浅析

【设计模式】Observer 观察者模式浅析

作者头像
行百里er
发布2020-12-02 14:37:54
5260
发布2020-12-02 14:37:54
举报
文章被收录于专栏:JavaJourneyJavaJourney

Observer模式:事件处理模型 事件处理模型经常使用Observer+责任链

军情观察室

朝鲜生气了,后果不太严重。中美俄等国一直对朝鲜暗中观察,就朝鲜的一系列活动各自采取措施。 在被观察者朝鲜周围,有多双眼睛正观察着它呢,这些观察者有中国、美国、俄罗斯等等。。

当朝鲜有活动时,这些观察者会采取相应的活动。

用代码构造一下这个场景:

代码语言:javascript
复制
public class Client {
    public static void main(String[] args) {
        Korea k = new Korea();
        k.fire();
    }
}

class Korea {

    China china = new China();
    Usa usa = new Usa();

    public void fire () {
        System.out.println("朝鲜:发射核弹!");
        china.warn();
        usa.threaten();
    }
}

class China {
    public void warn () {
        System.out.println("中国:不要在我家门口玩火,否则后果自负!");
    }
}

class Usa {
    public void threaten () {
        System.out.println("美国:韩国小老弟来我们军事演练走一波!");
    }
}

运行结果:

代码语言:javascript
复制
朝鲜:发射核弹!
中国:不要在我家门口玩火,否则后果自负!
美国:韩国小老弟来我们军事演练走一波!

上面这段代码的弊端是显而易见的,因为这里的观察者不止中国、美国,还有日本、俄罗斯等等,如果继续加入观察者,将会在被观察者类Korea中继续添加代码,耦合度太高,不易扩展!

观察者模式

上面的模型其实就用到了观察者模式的思想,观察者针对被观察者的行为动作做出相应的处理。 由于耦合度高,我们可以分离观察者和被观察者。有很多时候,观察者需要根据事件的具体情况来进行处理,而且我们处理事件的时候,需要事件源对象。 观察者中国、美国等需要对被观察者朝鲜的具体动作时间进行处理,朝鲜就是发出事件的事件源对象。

代码升级了:

代码语言:javascript
复制
public class Client {
    public static void main(String[] args) {
        Korea k = new Korea();
        k.fire();
    }
}

class Korea {

    private List<Observer> observers = new ArrayList<>();
    {
        observers.add(new China());
        observers.add(new Usa());
    }

    public void fire () {
        System.out.println("朝鲜:我要发射了!Boom!!!");
        //事件发生了
        FireEvent fireEvent = new FireEvent(System.currentTimeMillis(), "夏威夷", this);
        observers.forEach(observer -> observer.onFire(fireEvent));
    }
}

/**
 * 抽象出事件类
 * @param <T>
 */
abstract class Event<T> {
    abstract T getSource();
}

/**
 * 发射核弹事件,事件源是Korea
 */
class FireEvent extends Event<Korea> {

    //事件发生时间
    long timestamp;
    //事件发生地点
    String location;
    //事件源
    private Korea korea;

    public FireEvent(long timestamp, String location, Korea korea) {
        this.timestamp = timestamp;
        this.location = location;
        this.korea = korea;
    }

    @Override
    public Korea getSource() {
        return korea;
    }
}

/**
 * 观察者
 */
interface Observer {
    //观察者根据事件作出响应
    void onFire(FireEvent fireEvent);
}

class China implements Observer {
    public void warn () {
        System.out.println("中国:不要在我家门口玩火,否则后果自负!");
    }

    @Override
    public void onFire(FireEvent fireEvent) {
        if (fireEvent.location.contains("鸭绿江")) {
            warn();
        }
    }
}

class Usa implements Observer {
    public void threaten () {
        System.out.println("美国:韩国小老弟来我们军事演练走一波!");
    }

    @Override
    public void onFire(FireEvent fireEvent) {
        if (fireEvent.location.contains("夏威夷")) {
            threaten();
        }
    }
}

运行结果:

代码语言:javascript
复制
朝鲜:我要发射了!Boom!!!
美国:韩国小老弟来我们军事演练走一波!

可以看到,观察者只根据事件源Korea的事件发射,位置夏威夷做出了时间响应!

而且,有没有发现,被观察者里面有一个List<Observer> observers,很像责任链模式对吧?观察者模式的事件处理模型通常是Observer+责任链的。

随处可见的观察者

javascript的onclick

代码语言:javascript
复制
<script>
 function cb(event){
  alert(event);
 }
</script>

<input type="button" onclick="cb(this)" value="点我!" />

监听点击事件,做出事件响应

jdk awt包下的Button等

代码语言:javascript
复制
public class MyFrame extends Frame {
    public void launch() {
        Button b = new Button("press me");
        b.addActionListener(new MyActionListener());
        b.addActionListener(new MyActionListener2());
        this.add(b);
        this.pack();

        this.addWindowListener(new WindowAdapter(){

            @Override
            public void windowClosing(WindowEvent e) {
                System.exit(0);
            }

        });
        this.setLocation(400, 400);
        this.setVisible(true);
    }

    public static void main(String[] args) {
        new MyFrame().launch();
    }

    private class MyActionListener implements ActionListener { //Observer

        public void actionPerformed(ActionEvent e) {
            ((Button)e.getSource()).setLabel("press me again!");
            System.out.println("button pressed!");
        }

    }

    private class MyActionListener2 implements ActionListener {

        public void actionPerformed(ActionEvent e) {
            System.out.println("button pressed 2!");
        }

    }
}

这里的ActionListener就是观察者。

我们平时遇到的钩子函数,回调函数,Observer,甚至一些Listener,其实都是观察者模式的体现。

观察者模式的通用类图

参考《设计模式之禅》

  • Subject被观察者

定义被观察者必须实现的职责,它必须能够动态地增加、取消观察者。它一般是抽象类或者是实现类,仅仅完成作为被观察者必须实现的职责:管理观察者并通知观察者。

  • Observer观察者

观察者接收到消息后,即进行update(更新方法)操作,对接收到的信息进行处理。

  • ConcreteSubject具体的被观察者

定义被观察者自己的业务逻辑,同时定义对哪些事件进行通知。

  • ConcreteObserver具体的观察者

每个观察在接收到消息后的处理反应是不同,各个观察者有自己的处理逻辑。

小结

  • 观察者模式是松偶合的。改变主题或观察者中的一方,另一方不会受到影响。
  • JDK中也有自带的观察者模式。但是被观察者是一个类而不是接口,限制了它的复用能力。

Observer

java.util public interface Observer A class can implement the Observer interface when it wants to be informed of changes in observable objects. void update(Observable o, Object arg)

Observable

java.util Class Observable

  • 在JavaBean和Swing中也可以看到观察者模式的影子。

熬夜不易,欢迎关注转发、点赞、在看,给个鼓励,谢谢大伙! 也欢迎多踩踩我的博客:https://blog.csdn.net/hundred_li_journey

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-08-16,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 行百里er 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 军情观察室
  • 观察者模式
  • 随处可见的观察者
    • javascript的onclick
      • jdk awt包下的Button等
      • 观察者模式的通用类图
      • 小结
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档