深入理解JAVA中的观察者模式的实现及原理

我们想这样一个业务场景,在一站到底的舞台上,有两名学霸在进行终极battle,每一轮都是主持人提出一个问题,这个时候舞台(勉强这么理解吧)就是被观察者,两名选手就是观察者,观察面前的显示屏状态得到题目,然后进行作答,代码实现如下:

/**
 * 问题实体类
 * Created by guo on 2021/12/6 11:08
 */
@Data
public class Question {
    // 提问人名字
    private String username;
    // 问题内容
    private String content;
}
/**
 * 被观察者
 * Created by guo on 2021/12/6 11:07
 */
public class Stage extends Observable {
    private String name = "一站到底";
    private static Stage stage = null;
    private Stage(){};

    public static Stage getInstance(){
        if(null == stage) {
            stage = new Stage();
        }
        return stage;
    }
    public String getName(){
        return name;
    }
    public void publishQuestion(Question qUestion) {
        System.out.println(qUestion.getUsername() + "在" + this.name + "上提交了一个问题。");
        setChanged();
        notifyObservers(qUestion);
    }
}
/**
 * 观察者
 * Created by guo on 2021/12/6 11:08
 */
public class Contestant implements Observer {
    private String name;
    public Contestant(String name){
        this.name = name;
    }


    @Override
    public void update(Observable o, Object arg) {
        Stage stage = (Stage) o;
        Question question = (Question) arg;
        System.out.println("=========================");
        System.out.println(name + "你好! \n" + "您收到了一个来自“" + stage.getName() + "“的提问,问题如下:\n" + question.getContent() + "\n" + "提问者:"+question.getUsername());
    }
}

Test测试类:

/**
 * Created by maqingguo on 2021/12/6 11:10
 */
public class Test {
    public static void main(String[] args) {
        Stage stage = Stage.getInstance();
        Contestant zhangsan = new Contestant("张三");
        Contestant lisi = new Contestant("李四");

        stage.addObserver(zhangsan);
        stage.addObserver(lisi);

        // 业务逻辑代码
        Question question = new Question();
        question.setUsername("主持人");
        question.setContent("观察者模式的原理?");

        stage.publishQuestion(question);
    }
}

大家把这些代码粘下来,跑起来,会得到如下运行结果:

 代码运行流程如下:

 在这里面的重点是1:如何将观察者添加到被观察者的观察者队列;2:调用被观察者的publish方法如何通知观察者执行update方法,并且将参数传递过去。

为了搞清楚上面两点,我们需要先看看被观察者Stage继承的Observable类,源码及我的简易注释翻译如下:

public class Observable {
    // 是否需要通知的标识
    private boolean changed = false;
    // 观察者们
    private Vector<Observer> obs;

    /** Construct an Observable with zero Observers. */

    public Observable() {
        obs = new Vector<>();
    }

    /**
     * 添加观察者
     */
    public synchronized void addObserver(Observer o) {
        if (o == null)
            throw new NullPointerException();
        if (!obs.contains(o)) {
            obs.addElement(o);
        }
    }

    /**
     * 删除某个观察者
     */
    public synchronized void deleteObserver(Observer o) {
        obs.removeElement(o);
    }

    /**
     * 无参的通知方法
     */
    public void notifyObservers() {
        notifyObservers(null);
    }

    /**
     * 有参的通知方法
     */
    public void notifyObservers(Object arg) {
        /*
         * a temporary array buffer, used as a snapshot of the state of
         * current Observers.
         */
        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);
    }

    /**
     * 清空所有的观察者
     */
    public synchronized void deleteObservers() {
        obs.removeAllElements();
    }

    /**
     * 修改状态,代表允许触发观察者的update
     */
    protected synchronized void setChanged() {
        changed = true;
    }

    /**
     * 重置changed
     */
    protected synchronized void clearChanged() {
        changed = false;
    }

    /**
     * 返回当前changed,看看是否已经改变
     */
    public synchronized boolean hasChanged() {
        return changed;
    }

    /**
     * 返回观察者数量
     */
    public synchronized int countObservers() {
        return obs.size();
    }
}

可以看到当我们调用 stage.addObserver()时,是把观察者添加到obs队列中,在执行setChanged()时是将状态置为true,代表可以发布事件,notifyObservers(question)时是先判断状态,然后执行每个观察者的update,从这里面我们也看出,观察者必须要实现Obersver接口类,而这个接口很简单,只是声明了一个接口update要去实现

public interface Observer {
    /**
     * This method is called whenever the observed object is changed. An
     * application calls an <tt>Observable</tt> object's
     * <code>notifyObservers</code> method to have all the object's
     * observers notified of the change.
     *
     * @param   o     the observable object.
     * @param   arg   an argument passed to the <code>notifyObservers</code>
     *                 method.
     */
    void update(Observable o, Object arg);
}

 到目前为止,相信你也明白了观察者模式的原理,并且相信你也可以自己手写一个了。

当时我看完之后,我一直对这个Observable中的changed布尔值的存在,不能理解,不过我后来还是想到了一个场景:A线程改了被观察者的内容和状态,在去发布之前,恰好B线程又将被观察者复原了,那么正常情况下A线程的发布应该不需要被执行,但是如果没有这个changed布尔值,程序还是依然会去发布时间,而这显然不是我们想要的了,这也是为什么发布方法里面上了synchronized锁,并且在锁里面又加了changed的判断。

我想了想我们公司的项目目前的使用情况,发现这个模式并没有鸟用,因为他还是链式的去执行,并不能减少执行时间,都玩这个东西了,是kafka消息队列不香吗?

有疑问可以在评论区交流,作者看见会回复。

如需转载,请一定声明原处。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值