设计模式之观察者(Observer)

事件处理模型

小朋友睡醒了就哭,饿

写程序模拟这个过程:

v1:最简单的就是写程序一直观察着,什么时候哭了就进行处理

java 复制代码
/**
 * 披着面向对象外衣的面向过程
 */
public class Main1 {
    public static void main(String[] args) {
        boolean cry = false;

        while(!cry) {
            //进行处理
        }
    }
}

v2: 面向对象,至少抽象出child类来:

java 复制代码
/**
 * 面向对象的傻等
 * 一直观察,直到有线程调用了wakeUp方法
 * 程序没写完,涉及到线程同步,没有写完
 */

class Child {
    private boolean cry = false;

    public boolean isCry() {
        return cry;
    }

    public void wakeUp() {
        System.out.println("Waked Up! Crying wuwuwuwu...");
        cry = true;
    }
}

public class Main {
    public static void main(String[] args) {
        Child child = new Child();
        while(!child.isCry()) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("observing...");
        }

    }
}

v3: 加入观察者Dad到被观察者Child里面,什么时候醒了,直接调用观察者的方法

java 复制代码
/**
 * 加入观察者
 */

class Child {
    private boolean cry = false;
    private Dad d = new Dad(); //加入观察者Dad到被观察者Child里面

    public boolean isCry() {
        return cry;
    }

    public void wakeUp() {
        cry = true;
        d.feed(); // 直接调用观察者的方法
    }
}

class Dad {
    public void feed() {
        System.out.println("dad feeding...");
    }
}

public class Main {
    public static void main(String[] args) {
        Child c = new Child();
        //do sth
        c.wakeUp();
    }
}

v4:加入多个观察者,当一个事件发生的时候,每个观察者的处理方式不同

java 复制代码
/**
 * 加入多个观察者
 */

class Child {
    private boolean cry = false;
    // 多个观察者
    private Dad dad = new Dad();
    private Mum mum = new Mum();
    private Dog dog = new Dog();


    public boolean isCry() {
        return cry;
    }

    public void wakeUp() {
        cry = true;
        // 每个观察者的处理方式不同
        dad.feed();
        dog.wang();
        mum.hug();
    }
}

class Dad {
    public void feed() {
        System.out.println("dad feeding...");
    }
}

class Mum {
    public void hug() {
        System.out.println("mum hugging...");
    }
}

耦合度太高,观察者的处理方法不一定只适合当前的被观察者,例如小狗汪汪叫这个出来方法,可能也适合于鸡叫的事件

而且添加新的观察者比较麻烦 --> 扩展性不好,耦合度高

v5:分离观察者与被观察者。定义一个Observer接口,里面有actionWeakup方法,指的是小孩醒了之后要采取的动作,不同的观察者实现这Observer个接口

java 复制代码
/**
 * 分离观察者与被观察者
 */
class Child {
    private boolean cry = false;
    // 将观察者放到list中
    private List<Observer> observers = new ArrayList<>();

    {
        observers.add(new Dad());
        observers.add(new Mum());
        observers.add(new Dog());
    }


    public boolean isCry() {
        return cry;
    }

    public void wakeUp() {
        cry = true;
        // 当wakeup事件发生的时候,遍历观察者进行处理
        for(Observer o : observers) {
            o.actionOnWakeUp();
        }
    }
}

// 定义一个Observer接口
interface Observer {
    void actionOnWakeUp();
}

//不同的观察者实现这个Observer接口  
class Dad implements Observer {
    public void feed() {
        System.out.println("dad feeding...");
    }

    @Override
    public void actionOnWakeUp() {
        feed();
    }
}

class Mum implements Observer {
    public void hug() {
        System.out.println("mum hugging...");
    }

    @Override
    public void actionOnWakeUp() {
        hug();
    }
}

class Dog implements Observer {
    public void wang() {
        System.out.println("dog wang...");
    }

    @Override
    public void actionOnWakeUp() {
        wang();
    }
}

public class Main {
    public static void main(String[] args) {
        Child c = new Child();
        //do sth
        c.wakeUp();
    }
}

一个事件发生,会经过一系列的观察者的处理,这个事件才算完成

v5:一个事件发生之后,观察者需要根据事件的具体情况,做出处理。例如小孩哭的厉不厉害,时间等

该怎么把小孩哭的事件的具体情况传递给观察者呢?

封装一个事件类,事件的所有情况都在事件里面:

java 复制代码
/**
 * 有很多时候,观察者需要根据事件的具体情况来进行处理
 */

class Child {
    private boolean cry = false;
    private List<Observer> observers = new ArrayList<>();

    {
        observers.add(new Dad());
        observers.add(new Mum());
        observers.add(new Dog());
    }


    public boolean isCry() {
        return cry;
    }

    public void wakeUp() {
        cry = true;
        // 醒了之后,创建一个事件对象
        wakeUpEvent event = new wakeUpEvent(System.currentTimeMillis(), "bed");

        // 事件对象传递给观察者
        for(Observer o : observers) {
            o.actionOnWakeUp(event);
        }
    }
}

//事件类 fire Event
class wakeUpEvent{
    long timestamp;
    String loc;

    public wakeUpEvent(long timestamp, String loc) {
        this.timestamp = timestamp;
        this.loc = loc;
    }
}

// 事件的所有情况都在事件里面,封装好传给观察者
interface Observer {
    void actionOnWakeUp(wakeUpEvent event);
}

// Observer里面可以写if,判断event的情况,来进行处理,这块代码里面为了简便就没写
class Dad implements Observer {
    public void feed() {
        System.out.println("dad feeding...");
    }

    @Override
    public void actionOnWakeUp(wakeUpEvent event) {
        feed();
    }
}

class Mum implements Observer {
    public void hug() {
        System.out.println("mum hugging...");
    }

    @Override
    public void actionOnWakeUp(wakeUpEvent event) {
        hug();
    }
}

class Dog implements Observer {
    public void wang() {
        System.out.println("dog wang...");
    }

    @Override
    public void actionOnWakeUp(wakeUpEvent event) {
        wang();
    }
}

public class Main {
    public static void main(String[] args) {
        Child c = new Child();
        //do sth
        c.wakeUp();
    }
}

观察者模式三要素:

Source事件源对象 --> 是谁发出的事件

Observer观察者 --> 是谁在等着这个事件

Event事件 --> 事件本身

事件源对象有一堆的观察者观察着它,当事件源对象发出事件的时候,观察者会对事件进行处理

V7:一个事件源可以发出不同的事件,一个事件也可以被多个观察者处理,大多数时候,我们处理事件的时候,需要事件源对象

为什么需要事件源对象:因为有时候需要根据事件源对象的不同,采用不同的处理方法。

Sping的AOP其实可以语义上理解成观察者模式,在程序执行的过程中,弄一个切面,当代码执行到这个切面的时候,先执行指定好的一系列方法,

然后继续往下,这就相当于观察者模式,观察着这个切面,当到达这个切面的时候,先执行一系列定义好的方法

java 复制代码
/**
 * 有很多时候,观察者需要根据事件的具体情况来进行处理
 * 大多数时候,我们处理事件的时候,需要事件源对象
 */

class Child {
    private boolean cry = false;
    private List<Observer> observers = new ArrayList<>();

    {
        observers.add(new Dad());
        observers.add(new Mum());
        observers.add(new Dog());
    }


    public boolean isCry() {
        return cry;
    }

    public void wakeUp() {
        cry = true;

        wakeUpEvent event = new wakeUpEvent(System.currentTimeMillis(), "bed", this);

        for(Observer o : observers) {
            o.actionOnWakeUp(event);
        }
    }
}

class wakeUpEvent{
    long timestamp;
    String loc;
    // 将事件源对象,绑定到事件本身
    Child source;

    public wakeUpEvent(long timestamp, String loc, Child source) {
        this.timestamp = timestamp;
        this.loc = loc;
        this.source = source;
    }

    // 往往事件对象都会有一个getSource方法,例如系统提供的WindowEvent
    public Child getSource() {
        return this.source;
    }
}

interface Observer {
    void actionOnWakeUp(wakeUpEvent event);
}

class Dad implements Observer {
    public void feed() {
        System.out.println("dad feeding...");
    }

    @Override
    public void actionOnWakeUp(wakeUpEvent event) {
        feed();
    }
}

class Mum implements Observer {
    public void hug() {
        System.out.println("mum hugging...");
    }

    @Override
    public void actionOnWakeUp(wakeUpEvent event) {
        hug();
    }
}

class Dog implements Observer {
    public void wang() {
        System.out.println("dog wang...");
    }

    @Override
    public void actionOnWakeUp(wakeUpEvent event) {
        wang();
    }
}

public class Main {
    public static void main(String[] args) {
        Child c = new Child();
        //do sth
        c.wakeUp();
    }
}

v8:java里面的事件继承自EventObject这个类。

这里也自己定义了一个事件类,里面有getSource方法

钩子函数,hook,callback,listener都是观察者模式,都是一回事

java 复制代码
/**
 * 有很多时候,观察者需要根据事件的具体情况来进行处理
 * 大多数时候,我们处理事件的时候,需要事件源对象
 * 事件也可以形成继承体系
 */
class Child {
    private boolean cry = false;
    private List<Observer> observers = new ArrayList<>();

    {
        observers.add(new Dad());
        observers.add(new Mum());
        observers.add(new Dog());
        // 钩子函数hook,钩在那里,当事件发生的时候,钩子函数被执行
        // 钩子函数就是Observer模式
        // 传过去的是个函数
        observers.add((e)->{
            System.out.println("ppp");
        });
        //hook callback function
    }

    public boolean isCry() {
        return cry;
    }

    public void wakeUp() {
        cry = true;

        wakeUpEvent event = new wakeUpEvent(System.currentTimeMillis(), "bed", this);

        for(Observer o : observers) {
            o.actionOnWakeUp(event);
        }
    }
}
// 定义抽象Event方法,里面有getSource方法
abstract class Event<T> {
    abstract T getSource();
}

class wakeUpEvent extends Event<Child>{
    long timestamp;
    String loc;
    Child source;

    public wakeUpEvent(long timestamp, String loc, Child source) {
        this.timestamp = timestamp;
        this.loc = loc;
        this.source = source;
    }

    @Override
    Child getSource() {
        return source;
    }
}

interface Observer {
    void actionOnWakeUp(wakeUpEvent event);
}

class Dad implements Observer {
    public void feed() {
        System.out.println("dad feeding...");
    }

    @Override
    public void actionOnWakeUp(wakeUpEvent event) {
        feed();
    }
}

class Mum implements Observer {
    public void hug() {
        System.out.println("mum hugging...");
    }

    @Override
    public void actionOnWakeUp(wakeUpEvent event) {
        hug();
    }
}

class Dog implements Observer {
    public void wang() {
        System.out.println("dog wang...");
    }

    @Override
    public void actionOnWakeUp(wakeUpEvent event) {
        wang();
    }
}

public class Main {
    public static void main(String[] args) {
        Child c = new Child();
        //do sth
        c.wakeUp();
    }
}

v9:java jwt里面用了观察者模式,点击button,所有的观察者listener会被触发

java 复制代码
public class TestFrame 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 TestFrame().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!");
		}
		
	}
}

jvm运行着,监听着操作系统的键盘事件,当按键发生的时候,jvm会创建好KeyEvent事件

js里面也有event对象,事件源对象是event.traget。当按下button的时候,会自动生成event对象,把事件源对象传过去

在很多系统中,Observer模式往往和责任链共同负责对于事件的处理,其中的某一个observer负责是否将事件进一步传递

Observer不存在往回传的情况 --> 责任链部分讲到

相关推荐
daqinzl1 分钟前
java获取机器ip、mac
java·mac·ip
激流丶17 分钟前
【Kafka 实战】如何解决Kafka Topic数量过多带来的性能问题?
java·大数据·kafka·topic
Themberfue20 分钟前
Java多线程详解⑤(全程干货!!!)线程安全问题 || 锁 || synchronized
java·开发语言·线程·多线程·synchronized·
让学习成为一种生活方式37 分钟前
R包下载太慢安装中止的解决策略-R语言003
java·数据库·r语言
晨曦_子画43 分钟前
编程语言之战:AI 之后的 Kotlin 与 Java
android·java·开发语言·人工智能·kotlin
南宫生1 小时前
贪心算法习题其三【力扣】【算法学习day.20】
java·数据结构·学习·算法·leetcode·贪心算法
Heavydrink1 小时前
HTTP动词与状态码
java
ktkiko111 小时前
Java中的远程方法调用——RPC详解
java·开发语言·rpc
计算机-秋大田2 小时前
基于Spring Boot的船舶监造系统的设计与实现,LW+源码+讲解
java·论文阅读·spring boot·后端·vue
神里大人2 小时前
idea、pycharm等软件的文件名红色怎么变绿色
java·pycharm·intellij-idea