用观察者模式 动手写一个导弹预警系统(滑稽)

观察者模式定义:在对象之间定义一对多的依赖,这样一来,当一个对象改变状态,依赖它的对象都会收到通知,并自动更新。

观察者模式设计原则:为交互对象之间的松耦合设计而努力。

不足:如果使用了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);
}

啊 没了 就这么简单。。。

好吧 说完了。。。