观察者模式

概念

  • Observer 模式是行为模式之一, 他的作用是当一个对象的状态发生变化时, 能攻自动通知其他关联的对象, 自动刷新对象的状态
  • Observer 模式提供给关联对象一种同步通信的手段, 使某个对象与依赖他的其他对象之间保持状态同步

结构

这里写图片描述

  • Subject(被观察者): 被观察的对象. 当需要被观察者的状态发生变化时, 需要通知队列中所有的观察者对象. Subject需要维持(添加, 删除, 通知) 一个观察者对象的队列列表
  • ConcreteSubject: 被观察的具体实现. 包含一些基本的属性状态及其他操作
  • Observer(观察者): 接口或抽象类. 当Subject的状态发生变化时, Observer对象将通过一个callback的函数得到通知
  • ConcreteObserver: 观察者的具体实现. 得到通知后将完成一些具体的业务逻辑处理

JDK中的实现案例

代码(监听person对象的变化)

package design.observer.jdk;

import java.util.Observable;

public class Person extends Observable {
    private String name;
    private String sex;
    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
        // 设置对象发生了改变
        this.setChanged();
        System.out.println("对象是否发生了改变: " + this.hasChanged());
        // 设置的时候通知其他观察者
        this.notifyObservers();
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
        // 设置对象发生了改变
        this.setChanged();
        // 设置的时候通知其他观察者
        this.notifyObservers();
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
        // 设置对象发生了改变
        this.setChanged();
        // 设置的时候通知其他观察者
        this.notifyObservers();
    }

}
package design.observer.jdk;

import java.util.Observable;
import java.util.Observer;

public class MyObserver implements Observer {

    @Override
    public void update(Observable o, Object arg) {
        System.out.println("对象发生变化");
    }

}
package design.observer.jdk;

public class MainClass {
    public static void main(String[] args) {
        Person person = new Person();
        // 注册观察者
        person.addObserver(new MyObserver());
        person.addObserver(new MyObserver());
        // 删除所有观察者
        // person.deleteObservers();
        // 打印观察者数目
        System.out.println(person.countObservers());
        person.setName("龙龙");
        person.setAge(18);
        person.setSex("man");
    }
}

观察者模式的典型应用

Observer模式的典型应用

  • 侦听事件驱动程序设计中的外部事件
  • 侦听/监视某个对象的状态变化
  • 发布者/订阅者(publisher/subscriber)模型中, 当一个外部事件(新的产品, 消息的出现等等)被触发时, 通知邮件列表中的订阅者

手动实现案例

  • 宝宝睡觉, 爸爸在一旁观察, 宝宝醒来后, 爸爸给宝宝喂食
  • 代码(第一版)
package design.observer;

/**
 * @ClassName: Child
 * @Description:小孩类
 */
public class Child implements Runnable {
    // 是否醒来 默认是睡着的
    private Boolean wakeup = false;

    // 醒来的行为
    public void wakeUp() {
        wakeup = true;
    }

    public Boolean getWakeup() {
        return wakeup;
    }

    public void setWakeup(Boolean wakeup) {
        this.wakeup = wakeup;
    }

    @Override
    public void run() {
        try {
            Thread.sleep(5000);
        } catch (Exception e) {
            e.printStackTrace();
        }
        // 小孩醒来
        wakeUp();
    }

}
package design.observer;

/**
 * @ClassName: Dad
 * @Description: 父亲类 需要监控孩子 孩子醒来后 需要喂食
 */
public class Dad implements Runnable {
    // 需要持有孩子的引用
    private Child baby;

    public Dad(Child baby) {
        this.baby = baby;
    }

    // 喂食
    public void feed(Child baby) {
        System.out.println("已经给宝贝喂食!");
    }

    // 实时看看孩子是否醒来
    @Override
    public void run() {
        while (!baby.getWakeup()) {
            // 如果小孩子没有醒来 则一直循环
            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep(1000);
                    System.out.println("宝宝睡觉中, 5秒后即将醒来 记得喂食:" + (i + 1) + "秒");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        // 代表小孩醒来 喂食
        this.feed(baby);

    }
}
package design.observer;

public class LookChild {
    public static void main(String[] args) {
        Child baby = new Child();
        new Thread(baby).start();

        Dad dad = new Dad(baby);
        new Thread(dad).start();
    }
}
  • 问题: 爸爸一直在监视这宝宝的状态 其它什么事情都干不了 这是不符合逻辑的, 爸爸也可以一边看电视 一边看孩子嘛! 需要改进….
  • 代码(第二版)
package design.observer;

/**
 * @ClassName: Child
 * @Description:小孩类
 */
public class Child implements Runnable {
    private Dad father;

    public Child(Dad father) {
        this.father = father;
    }

    // 是否醒来 默认是睡着的
    private Boolean wakeup = false;

    // 醒来的行为 状态改变 需要告诉老爸 所以需要持有父亲的引用
    public void wakeUp() {
        wakeup = true;
        // 醒来后通知老爸喂食
        father.feed(this);
    }

    public Boolean getWakeup() {
        return wakeup;
    }

    public void setWakeup(Boolean wakeup) {
        this.wakeup = wakeup;
    }

    @Override
    public void run() {
        while (!this.getWakeup()) {
            // 如果小孩子自己没有醒来 则一直循环
            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep(1000);
                    System.out.println("宝宝睡觉中, 5秒后即将醒来 记得喂食: 已经过了" + (i + 1) + "秒");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            // 小孩醒来
            wakeUp();
        }

    }

}
package design.observer;

/**
 * @ClassName: Dad
 * @Description: 父亲类 需要监控孩子 孩子醒来后 需要喂食
 */
public class Dad /* implements Runnable */ {// 不要再监控孩子了 不用实现Runnable接口了

    public Dad() {
    }

    // 喂食
    public void feed(Child baby) {
        System.out.println("已经给宝贝喂食!");
    }

}
package design.observer;

public class LookChild {
    public static void main(String[] args) {
        Child baby = new Child(new Dad());
        new Thread(baby).start();
    }
}
  • 但是这样孩子醒来后, 只能通知他的爸爸, 如果其它他人在 也可以处理喂食这件事, 而且如果事件包含多个状态, 需要对每个状态进行不同的处理 一个事件的改变引起多方面变化
  • 代码(最终版)
package design.observer;

import java.util.ArrayList;
import java.util.List;

/**
 * @ClassName: Child
 * @Description:小孩类
 */
public class Child implements Runnable {
    // 持有 所有监护者 的集合
    private List<WakeUpListener> wakeUpListeners = new ArrayList<>();

    // 将监护者加入到集合中 以便处理醒来的事件
    public void addWakeUpListener(WakeUpListener wakeUpListener) {
        wakeUpListeners.add(wakeUpListener);
    }

    // 醒来的行为 状态改变 需要告诉老爸 所以需要持有父亲的引用
    public void wakeUp() {
        // 此处不直接写方法行为了 我们根据事件的情况进行处理
        // 获取具有处理能力的所有监听对象
        // father.actionToWakeUp(new WakeUpEvent(false, this));
        for (int i = 0; i < wakeUpListeners.size(); i++) {
            WakeUpListener wakeUpListener = wakeUpListeners.get(i);
            // true:是饭点了 false:不是饭点
            wakeUpListener.actionToWakeUp(new WakeUpEvent(true, this));
        }

    }

    @Override
    public void run() {
        boolean flag = true;
        while (flag) {
            // 如果小孩子自己没有醒来 则一直循环
            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep(1000);
                    System.out.println("宝宝睡觉中, 5秒后即将醒来 记得喂食: 已经过了" + (i + 1) + "秒");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            // 小孩醒来
            wakeUp();
            flag = false;
        }

    }

}
package design.observer;

/**
 * @ClassName: Dad
 * @Description: 父亲类 需要监控孩子 孩子醒来后 需要喂食
 */
public class Dad extends GrandFather implements WakeUpListener {// 不要再监控孩子了

}
package design.observer;

/**
 * @ClassName: Dad
 * @Description: 爷爷类
 */
public class GrandFather implements WakeUpListener {// 不要再监控孩子了 不用实现Runnable接口了

    // 直接在爷爷中声明对醒来事件的处理 爸爸继承爷爷
    @Override
    public void actionToWakeUp(WakeUpEvent wakeUpEvent) {
        // 这里我们通过对事件对象的分析作出相应的行为
        if (wakeUpEvent.getIsFoodtime()) {
            // 是饭点醒来 喂食
            feed();
        } else {
            // 不是 抱着他玩
            play();
        }
    }

    // 喂食
    public void feed() {
        System.out.println(this.getClass().getName().substring(this.getClass().getName().lastIndexOf(".") + 1) + " : 已经给宝贝喂食!");
    }

    // 抱小孩子出去玩
    public void play() {
        System.out.println(this.getClass().getName().substring(this.getClass().getName().lastIndexOf(".") + 1) + " : 抱小孩子出去玩!");
    }

}
package design.observer;

/**
 * @ClassName: WakeUpEvent
 * @Description: 醒来事件的类
 */
public class WakeUpEvent {
    // 醒来时间是否是饭点
    private Boolean isFoodtime;
    // 谁产生这个事件(事件源)
    private Child source;

    public WakeUpEvent(Boolean isFoodtime, Child source) {
        this.isFoodtime = isFoodtime;
        this.source = source;
    }

    public Boolean getIsFoodtime() {
        return isFoodtime;
    }

    public void setIsFoodtime(Boolean isFoodtime) {
        this.isFoodtime = isFoodtime;
    }

    public Child getSource() {
        return source;
    }

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

}
package design.observer;

/**
 * @ClassName: WakeUpListener
 * @Description: 屋里的人都能听到小孩子哭这件事情, 都有处理小孩子醒来行为的方法
 */
public interface WakeUpListener {
    void actionToWakeUp(WakeUpEvent wakeUpEvent);
}
package design.observer;

import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;

/**
 * @ClassName: PropertiesManager
 * @Description:读取外部配置的监听者
 */
public class PropertiesManager {
    public static Map<String, String> getPropertiesInfo() {
        Map<String, String> classNameMap = new HashMap<>();
        Properties properties = new Properties();

        InputStream inputStream = PropertiesManager.class.getClassLoader().getResourceAsStream("properties/observer.properties");
        try {
            properties.load(inputStream);
            Iterator<String> iterator = properties.stringPropertyNames().iterator();
            while (iterator.hasNext()) {
                String key = (String) iterator.next();
                classNameMap.put(key, properties.getProperty(key));
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return classNameMap;

    }
}

observer.properties

observer1=design.observer.GrandFather
observer2=design.observer.Dad
package design.observer;

import java.util.Map;
import java.util.Map.Entry;

public class LookChild {
    public static void main(String[] args) throws Exception {
        // 创建事件源
        Child child = new Child();
        // 加入监听者(如果需要加入新的监听者 那么需要修改代码 所以需要改进)
        // child.addWakeUpListener(new GrandFather());
        // child.addWakeUpListener(new Dad());

        // 采用动态加载的思想读取配置文件
        Map<String, String> map = PropertiesManager.getPropertiesInfo();
        for (Entry<String, String> entry : map.entrySet()) {
            child.addWakeUpListener((WakeUpListener) (Class.forName(entry.getValue()).newInstance()));
        }
        new Thread(child).start();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值