本文主要是介绍【原创】自己动手循序渐进实现观察者模式,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
引言
自上一篇《自己动手实现牛逼的单例模式》问世之后,得到了不错的评价。于是博主在五一放弃出去游玩机会,赶制了这篇《自己动手循序渐进实现观察者模式》,依然还是一步一步推导出最终版的观察者模式。
观察者模式
定义:在许多设计中,经常涉及多个对象都对一个特殊对象中的数据变化感兴趣,而且这多个对象都希望跟踪那个特殊对象中的数据变化,在这样的情况下就可以使用观察者模式。
在这里,我们以母亲观察宝宝为例子,宝宝正在睡觉,醒来后,母亲做出相应的喂食行为。
1、观察者V1
假设我们用下图所示的情景
先定义一个Baby类,baby先睡五秒,五秒后醒来,代码如下所示
package rjzheng.observer1;public class Baby implements Runnable { // 默认是睡着private boolean wakeup = false; // 醒来的行为public void wakeUp() { this.wakeup = true;} public boolean isWakeup() { return wakeup;} public void setWakeup(boolean wakeup) { this.wakeup = wakeup;} @Overridepublic void run() { // TODO Auto-generated method stubtry {Thread.sleep(5000);} catch (InterruptedException e) { // TODO Auto-generated catch blocke.printStackTrace();}wakeUp();} }
而母亲类即Mother类,则一直监听宝宝的状态,宝宝一醒来就喂食,代码如下所示
package rjzheng.observer1;public class Mother implements Runnable { private Baby baby; public Mother(Baby baby) { this.baby = baby;} public void feed(Baby baby){System.out.println("已经给宝贝喂食");} @Overridepublic void run(){ while(!baby.isWakeup()){ for(int i=0;i<5;i++){ try {Thread.sleep(1000);System.out.println("宝宝还有"+(5-i)+"秒醒来");} catch (InterruptedException e) { // TODO Auto-generated catch blocke.printStackTrace();}}} this.feed(baby);} }
然后写一个ObserverTest1测试一下,代码如下所示
package rjzheng.observer1;public class ObserverTest1 { public static void main(String[] args) {Baby baby =new Baby(); new Thread(baby).start(); new Thread(new Mother(baby)).start();} }
输出如下
宝宝还有5秒醒来
宝宝还有4秒醒来
宝宝还有3秒醒来
宝宝还有2秒醒来
宝宝还有1秒醒来
已经给宝贝喂食
2、观察者V2
这时情景变为下图
可是上一版,母亲要一直看着宝宝,母亲觉得太累了,母亲要是想中间聊个微信啥的,多不方便,于是V2版出现了。我们能不能孩子醒来后,主动告诉给母亲呢。于是修改Baby的wakeUp方法,即醒来状态改变后,增加一个通知母亲的方法。
综上所述,Baby类做如下几点修改
-
持有Mother的引用,修改构造函数
-
在wakeUp方法中增加,通知母亲的逻辑
-
在run方法中,自己五秒后醒来,自己修改自己的状态
package rjzheng.observer2;public class Baby implements Runnable { // 默认是睡着private boolean wakeup = false; private Mother mother; //1. 持有Mother的引用,修改构造函数public Baby(Mother mother){ this.mother = mother;} //2. 在wakeUp方法中增加,通知母亲的逻辑public void wakeUp() {wakeup = true; this.mother.feed(this);} public boolean isWakeup() { return wakeup;} public void setWakeup(boolean wakeup) { this.wakeup = wakeup;} //3. 在run方法中,自己五秒后醒来,自己修改自己的状态@Overridepublic void run() { while(!this.isWakeup()){ for(int i=0;i<5;i++){ try {Thread.sleep(1000);System.out.println("宝宝还有"+(5-i)+"秒醒来");} catch (InterruptedException e) { // TODO Auto-generated catch blocke.printStackTrace();}} this.wakeUp();}} }
而Mother类,则不需要做线程操作,Baby类通知Mother类后,做出相应处理即可。因此,Mother类修改如下
-
删除run方法等线程相关操作
-
增加一个无参构造方法(因为Mother类不需要持有Baby类的引用了)
综上所述,Mother类的代码如下
package rjzheng.observer2;public class Mother{ private Baby baby; public Mother(){} public Mother(Baby baby) { this.baby = baby;} public void feed(Baby baby){System.out.println("已经给宝贝喂食");} }
测试类ObserverTest2的代码如下
package rjzheng.observer2;public class ObserverTest2 { public static void main(String[] args) {Baby baby =new Baby(new Mother()); new Thread(baby).start();} }
输出如下所示
宝宝还有5秒醒来
宝宝还有4秒醒来
宝宝还有3秒醒来
宝宝还有2秒醒来
宝宝还有1秒醒来
已经给宝贝喂食
3、观察者V3
这个时候,宝宝的父亲回来了。父亲觉得,在宝宝醒来后,应该带宝宝出去玩。于是就和孩子他妈约定,根据孩子醒来的时间决定,带孩子做什么。如果醒来的时间是饭点,就给孩子喂食;如果醒来的时间不是饭点,就带孩子出去玩。
那么重点来了,醒来的时间是否为饭点这个属性,放在父亲类里,不大合适;放在宝宝类里也不大合适;放母亲类里也不大合适。根据面向对象的设计原则,我们定义一个醒来的事件类WakeUpEvent,将醒来的时间是否为饭点放在WakeUpEvent里。除此之外,再定义一个事件源source属性,那么WakeUpEvent类如下所示
package rjzheng.observer3;/*** 醒来的事件对象* @author zhengrongjun**/public class WakeUpEvent { //醒来时间是否为饭点private boolean isFoodTime; //事件源private Baby source; public WakeUpEvent(boolean isFoodTime,Baby source){ this.isFoodTime = isFoodTime; this.source = source;} public boolean isFoodTime() { return isFoodTime;} public void setFoodTime(boolean isFoodTime) { this.isFoodTime = isFoodTime;} public Baby getSource() { return source;} public void setSource(Baby source) { this.source = source;} }
那么,接下来,Father类拥有一个带宝贝出去玩的方法,如下所示
package rjzheng.observer3;public class Father { public void play(WakeUpEvent wakeUpEvent){ if(!wakeUpEvent.isFoodTime()){System.out.println("抱宝贝出去玩");}} }
自然,Mother类拥有一个给宝贝喂食的方法,如下所示
package rjzheng.observer3;public class Mother{ public void feed(WakeUpEvent wakeUpEvent){ if(wakeUpEvent.isFoodTime()){System.out.println("给宝贝喂食");}} }
那么在这种情况下Baby类修改为如下所示
package rjzheng.observer3;public class Baby implements Runnable { private Mother mother; private Father father; public Baby(Mother mother,Father father) { this.mother = mother; this.father = father;} public void wakeUp() { this.mother.feed(new WakeUpEvent(true, this)); this.father.play(new WakeUpEvent(true, this));} @Overridepublic void run() { boolean flag = true; while (flag) { for (int i = 0; i < 5; i++) { try {Thread.sleep(1000);System.out.println("宝宝还有" + (5 - i) + "秒醒来");} catch (InterruptedException e) { // TODO Auto-generated catch blocke.printStackTrace();}} this.wakeUp();flag=false;}} }
测试类ObserverTest3如下所示
package rjzheng.observer3;public class ObserverTest3 { public static void main(String[] args) {Baby baby =new Baby(new Mother(),new Father()); new Thread(baby).start();} }
运行结果如下
宝宝还有5秒醒来
宝宝还有4秒醒来
宝宝还有3秒醒来
宝宝还有2秒醒来
宝宝还有1秒醒来
给宝贝喂食
4、观察者V4
这时情景变为下图
又过了几天,孩子不乐意了。。。。孩子觉得,我每次还要去记爸爸能带我干嘛,妈妈能带我干嘛,太麻烦了。以后,爷爷奶奶来来了,我难道还要去记你们能干嘛,再通知相应的人嘛。不如这样,我把你们全部叫来,你们自己商量好,该带我干嘛吧。
OK。。这也是观察者V3版的缺点,代码复用性太差。如果多一个爷爷GrandFather类,那Baby类里头还要多一个Grandfather类的引用,并且在wakeup里增加通知逻辑,太麻烦。索性,定义一个WakeUpListener接口,继承这个接口的人,代表都具有处理WakeUpEvent事件的能力
那么WakeUpListener接口的源码如下
package rjzheng.observer4;public interface WakeUpListener { public void actiontoWakenUp(WakeUpEvent wakeUpEvent); }
这时Father类和Mother类代码如下,都实现了WakeUpListener接口
package rjzheng.observer4;public class Father implements WakeUpListener { @Overridepublic void actiontoWakenUp(WakeUpEvent wakeUpEvent) { // TODO Auto-generated method stubif(!wakeUpEvent.isFoodTime()){System.out.println("抱宝贝出去玩");} } }
package rjzheng.observer4;public class Mother implements WakeUpListener{ @Overridepublic void actiontoWakenUp(WakeUpEvent wakeUpEvent) { if(wakeUpEvent.isFoodTime()){System.out.println("给宝贝喂食");}} }
这时的Baby类如下所示
package rjzheng.observer4;import java.util.ArrayList;import java.util.List;public class Baby implements Runnable { private List<WakeUpListener> wakeUpListeners = new ArrayList(); public void addListeners(WakeUpListener wakeUpListener){ this.wakeUpListeners.add(wakeUpListener);} public void wakeUp() { for(WakeUpListener listener : wakeUpListeners)listener.actiontoWakenUp(new WakeUpEvent(true, this));} @Overridepublic void run() { boolean flag = true; while (flag) { for (int i = 0; i < 5; i++) { try {Thread.sleep(1000);System.out.println("宝宝还有" + (5 - i) + "秒醒来");} catch (InterruptedException e) { // TODO Auto-generated catch blocke.printStackTrace();}} this.wakeUp();flag=false;}} }
测试类ObserverTest4为
package rjzheng.observer4;public class ObserverTest4 { public static void main(String[] args) {Baby baby =new Baby();baby.addListeners(new Father());baby.addListeners(new Mother()); new Thread(baby).start();} }
最后结果如下
宝宝还有5秒醒来
宝宝还有4秒醒来
宝宝还有3秒醒来
宝宝还有2秒醒来
宝宝还有1秒醒来
给宝贝喂食
总结
本篇文章给出了四种观察者模式,一般都是用V4版啦。。前面V1,V2,V3都只是铺垫,为了引出最后一版的观察者模式,希望大家有所收获。
对CSDNJava架构技术,对架构技术感兴趣的同学,欢迎加QQ群859729143,一起学习,相互讨论。
群内已经有小伙伴将知识体系整理好(源码,笔记,PPT,学习视频),欢迎?加群免费领取
分享给喜欢Java,喜欢编程,有梦想成为架构师的程序员们,希望能够帮助到你们。
这篇关于【原创】自己动手循序渐进实现观察者模式的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!