用观察者模式 动手写一个导弹预警系统(滑稽)
观察者模式定义:在对象之间定义一对多的依赖,这样一来,当一个对象改变状态,依赖它的对象都会收到通知,并自动更新。
观察者模式设计原则:为交互对象之间的松耦合设计而努力。
不足:如果使用了java中自带的观察者模式类,则违背了多用组合少用继承和不针对实现编程的设计原则
要点:观察者模式定义了一对多的对象关系,主题(可观察者)用一个共同的接口来更新观察者,当有多个观察者是不能依赖代码中的次序,如果有必要就自己去实现观察者模式而不用java.util.Observable。
下面举一个笔者捏造的例子来详细阐述观察者模式:(例子在现实中具体如何运作还请自行百度)
假如从美国本土发射一枚洲际导弹射向我们,那么我们的雷达站首先要捕获目标,其次将捕获数据上传至导弹预警系统MissileEarlyWarningSystem 以下简称MEWS,之后MEWS通知五大战区做好战备。
- 首先我们需要一个程序入口(main方法),也就是雷达站传感器。
- 其次雷达站捕获数据要传输至MEWS,此时MEWS相当于一个可观察者(Observable)
- 之后MEWS要通知五大战区,五大战区相当于观察者(Observer)
- 最后五大战区接到通知做出处理,相当于观察者接收到可观察者推送的更新数据后做出处理。
下面我们用代码实现以下:
首先定义 可观察者类 MissileEarlyWarningSystem (MEWS):
package com.umbrella.common.test; import java.util.Observable; //基于 - 观察者模式 -静态内部类单例模式 //洲际导弹预警系统 public class MissileEarlyWarningSystem extends Observable { private static class MissileEarlyWarningSystemLoader{ private static MissileEarlyWarningSystem missileEarlyWarningSystem = new MissileEarlyWarningSystem(); } public static MissileEarlyWarningSystem getInstance() { return MissileEarlyWarningSystemLoader.missileEarlyWarningSystem; } private long missileHeight;//导弹高度 private float missileSpeed;//导弹速度 private String missileType;//导弹型号 private float missileDamage;//导弹伤害 private float missileLongitude;//导弹经度 private float missileLatitude;//导弹纬度 private MissileEarlyWarningSystem() { } //===发布接受到的导弹数据=== public void pushRadioData(long missileHeight, float missileSpeed, String missileType, float missileDamage, float missileLongitude, float missileLatitude) { this.missileHeight = missileHeight; this.missileSpeed = missileSpeed; this.missileType = missileType; this.missileDamage = missileDamage; this.missileLongitude = missileLongitude; this.missileLatitude = missileLatitude; System.out.println("导弹预警系统收到雷达站通知"); System.out.println("开始向五大战区推送导弹数据"); warningNotify(); } public void warningNotify(){ setChanged(); //必须调用的方法,底层实现是设置变更标志位为True notifyObservers(); //向所有注册的观察者发布最新消息,发布后将变更标志位置位False } //====导弹数据展示===== public String warningDetail(){ return "MissileEarlyWarningSystem{" + "missileHeight=" + missileHeight + ", missileSpeed=" + missileSpeed + ", missileType='" + missileType + '\'' + ", missileDamage=" + missileDamage + ", missileLongitude=" + missileLongitude + ", missileLatitude=" + missileLatitude + '}'; } }
下面定义五大战区 观察者类 :(因为五个观察者代码相似所以只po出一个)
public class CenterWarZone implements Observer { MissileEarlyWarningSystem missileEarlyWarningSystem ; //注册观察者 public CenterWarZone(MissileEarlyWarningSystem missileEarlyWarningSystem) { this.missileEarlyWarningSystem = missileEarlyWarningSystem; missileEarlyWarningSystem.addObserver(this);//注册观察者方法,告诉可观察者出了事通知我 } @Override //!!!重点!!!update方法 public void update(Observable o, Object arg) {//必须要实现update方法,因为收到通知执行的就是update方法 if(o instanceof MissileEarlyWarningSystem){ MissileEarlyWarningSystem mews = (MissileEarlyWarningSystem) o; System.out.println("中部战区收到导弹来袭警告,导弹详情:"); System.out.println("================================"); System.out.println(mews.warningDetail()); System.out.println("================================"); System.out.println(" "); } } }
下面定义雷达系统类(程序主入口,用于向可观察者发送数据,并初始化观察者进行注册操作)
package com.umbrella.common.test; public class RadioSystem { public static void main(String[] args) { try{ MissileEarlyWarningSystem mews= MissileEarlyWarningSystem.getInstance() ; WesternWarZone westernWarZone = new WesternWarZone(mews); //西部战区 EasternWarZone easternWarZone = new EasternWarZone(mews); NorthernWarZone northernWarZone = new NorthernWarZone(mews); SouthernWarZone southernWarZone = new SouthernWarZone(mews); CenterWarZone centerWarZone = new CenterWarZone(mews); System.out.println("雷法捕获洲际导弹数据...."); System.out.println("向导弹预警系统发出通知"); mews.pushRadioData(1000,3080.3f,"民兵",40000,116.46f,39.92f); }catch(Exception e){ e.printStackTrace(); } } }
结果如下所示:
怎么样 是不是帅死了!
自己也动手玩玩吧!
==刨根问底儿环节:底层实现原理==
Observable类大揭秘
成员变量:
private boolean changed = false; //变更标志位 private Vector<Observer> obs; //可以看出所有的观察者都存在Vector数组中vector和arraylist的比较:
1、都是采用数组格式存储数据,索引数据块插入数据慢
2、ArrayList会比Vector快,他是非同步的
3、涉及到多线程,Vector是同步的
构造器:
public Observable() { obs = new Vector<>(); }
典型方法:
1、注册观察者
public synchronized void addObserver(Observer o) { if (o == null) throw new NullPointerException(); if (!obs.contains(o)) { obs.addElement(o); } }
2、删除观察者
public synchronized void deleteObserver(Observer o) { obs.removeElement(o); }
3、通知观察者
可以看出是通过循环调用观察者的Update方法实现的通知行为,所以说update是很重要的
public void notifyObservers(Object arg) { Object[] arrLocal; synchronized (this) { //同步的 if (!changed) return; arrLocal = obs.toArray(); clearChanged(); } for (int i = arrLocal.length-1; i>=0; i--) ((Observer)arrLocal[i]).update(this, arg); }
4、状态标记位变更
protected synchronized void setChanged() { changed = true; } protected synchronized void clearChanged() { changed = false; }
5、列出当前观察者数量
public synchronized int countObservers() { return obs.size(); }
6、告诉观察者是否已经发出过通知
public synchronized boolean hasChanged() { return changed; }
没了 就这些东西 很简单吧 ,其实可以自己手动实现一个可观察者类!
下面来看看观察者实现的观察者(Observer)接口都有什么
package java.util; public interface Observer { void update(Observable o, Object arg); }
啊 没了 就这么简单。。。
好吧 说完了。。。