设计模式之观察者设计模式

首先介绍我在实际开发过程中使用到的观察者模式:
在写一个Android应用商城的时候,下载某个App时可以在主页点击下载,也可以查看这个App详情页面下载.而无论点击那个下载链接来下载,它们两个下载的进度必须是同步的,这就需要两个观察者来观察下载这一个过程。
首先简单介绍一下观察者设计模式,包括java自带的观察者的接口与被观察者的抽象类.文章后面详细介绍观察者设置模式.
java的util包提供了Observable类与Observer接口。其中继承Observable的为被观察者,实现Observer接口的为观察者:
被观察者的代码:

public class DownLoadManage extends Observable{
    @Override
    //hasChanged必须重写,返回true表示改变了,才会通知观察者
    public boolean hasChanged() {
        return  true;
    }
}

观察者的示例代码:

public class ToSeeDownLoad1 implements Observer {
//实现接口的update的方法,当被观察者有动静时调用
    @Override
    public void update(Observable observable, Object o) {
        System.out.println("1看见你的变化");
    }
}

测试类:

 //观察者与被观察者
   DownLoadManage   downLoadManage = new DownLoadManage();
   //为被观察者注册观察者
    downLoadManage.addObserver(new ToSeeDownLoad1());
   downLoadManage.addObserver(new ToSeeDownLoad2());
//被观察者通知观察者
downLoadManage.notifyObservers();

如上就是观察者模式的简单案例,但这个有个很大的缺陷就是被观察者是继承了Observable抽象类,我们知道java只能单继承。所以java的util自动为我们提供的抽象类使用很局限。所以我们可以通过查看ObServable的源码来自己实现被观察者。

通过查看 源码可以知道,被观察者的ObServable其实内部维护了一个泛型为Observer(观察者的接口)的集合。当增加观察者时就向集合中增加对象,调用notifyObservers()方法时就是遍历集合的每个数据来调用接口中的update的方法。我们仿照这个逻辑来自己定义观察者的接口而改变被观察者继承ObServable的局限性。

被观察者的接口

public interface MyObserverInterface {
    //以后观察者实现这个接口
    void update();
}

被观察者:

public class DownLoadManageForMyObserver {
    List<MyObserverInterface> list = new ArrayList<MyObserverInterface>();
   //添加观察者
    public synchronized void addObServer(MyObserverInterface myObserverInterface){
        if(myObserverInterface==null){
            throw new NullPointerException();
        }else  if (!list.contains(myObserverInterface)) {
            list.add(myObserverInterface);
        }
    }
    //通知观察者数据改变了来调用观察者的update方法
    public  synchronized void notifyObServers(){
        for(MyObserverInterface myObserverInterface : list){
            myObserverInterface.update();
        }
    }
}

观察者:

public class MyToSeeDownLoad1 implements MyObserverInterface {
    @Override
    public void update() {
        System.out.println("1 see");
    }
}

测试类:

  //观察者与被观察者
    DownLoadManageForMyObserver    downLoadManageForMyObserver = new DownLoadManageForMyObserver();
        downLoadManageForMyObserver.addObServer(new MyToSeeDownLoad1());
         downLoadManageForMyObserver.notifyObServers();

经过修改之后,观察者的扩展性也更强了除了update()方法之外,也可以增加其他方法。最后只有在被观察者中调用就行。
现在我们从头到尾学习观察者设计模式:
参考书籍 : 秦小波老师的《设计模式之禅》

案例:
以李斯(观察者)观察韩非子(被观察者)为例来说明:

被观察者接口
public interface IHanFeiZi {
//韩非子也是人,也要吃早饭的
public void haveBreakfast();
//韩非之也是人,是人就要娱乐活动
public void haveFun();
}

对接口进行扩充,增加了两个状态isHavingBreakfast(是否在吃早饭)和isHavingFun(是否在娱乐),以方便进行监控:

/*
具体的被观察者
*/
public class HanFeiZi implements IHanFeiZi{
//韩非子是否在吃饭,作为监控的判断标准
private boolean isHavingBreakfast = false;
//韩非子是否在娱乐
private boolean isHavingFun = false;
//韩非子要吃饭了
public void haveBreakfast(){
System.out.println("韩非子:开始吃饭了...");
this.isHavingBreakfast =true;
}
//韩非子开始娱乐了
public void haveFun(){
System.out.println("韩非子:开始娱乐了...");
this.isHavingFun = true;
}
//以下是bean的基本方法,getter/setter,不多说
public boolean isHavingBreakfast() {
return isHavingBreakfast;
}
public void setHavingBreakfast(boolean isHavingBreakfast) {
this.isHavingBreakfast = isHavingBreakfast;
}
public boolean isHavingFun() {
return isHavingFun;
}
public void setHavingFun(boolean isHavingFun) {
this.isHavingFun = isHavingFun;
}
}

通过isHavingBreakfast和isHavingFun这两个布尔型变量来判断韩非子是否在吃饭或者娱乐,韩非
子属于被观察者,那还有观察者李斯,我们来看李斯的接口

public interface ILiSi {
//一发现别人有动静,自己也要行动起来
public void update(String context);
}

李斯这类人比较简单,一发现自己观察的对象发生了变化,比如吃饭、娱乐,自己立刻也要行动起来,怎么行动呢?

public class LiSi implements ILiSi{
//首先李斯是个观察者,一旦韩非子有活动,他就知道,他就要向老板汇报
public void update(String str){
System.out.println("李斯:观察到韩非子活动,开始向老板汇报了...");
this.reportToQinShiHuang(str);
System.out.println("李斯:汇报完毕...\n");
}
//汇报给秦始皇
private void reportToQinShiHuang(String reportContext){
System.out.println("李斯:报告,秦老板!韩非子有活动了--->"+reportContext);
}
}

两个重量级的人物都定义出来了,间谍这个“卑鄙”小人是不是也要登台了,如代码清单:

class Spy extends Thread{
private HanFeiZi hanFeiZi;
private LiSi liSi;
private String type;
//通过构造函数传递参数,我要监控的是谁,谁来监控,要监控什么
public Spy(HanFeiZi _hanFeiZi,LiSi _liSi,String _type){
this.hanFeiZi =_hanFeiZi;
this.liSi = _liSi;
this.type = _type;
}
@Override
public void run(){
while(true){
if(this.type.equals("breakfast")){ //监控是否在吃早餐
//如果发现韩非子在吃饭,就通知李斯
if(this.hanFeiZi.isHavingBreakfast()){
this.liSi.update("韩非子在吃饭");
//重置状态,继续监控
this.hanFeiZi.setHavingBreakfast(false);
}
}else{//监控是否在娱乐
if(this.hanFeiZi.isHavingFun()){
this.liSi.update("韩非子在娱乐");
this.hanFeiZi.setHavingFun(false);
}
}
}
}
}

监控程序继承了java.lang.Thread类,可以同时启动多个线程进行监控,Java的多线程机制还是比较简单的,继承Thread类,重写run()方法,然后new SubThread(),再然后subThread.start()就可以启动一个线程了。我们建立一个场景类来回顾一下这段历史,如代码清单:

public class Client {
public static void main(String[] args) throws InterruptedException {
//定义出韩非子和李斯
LiSi liSi = new LiSi();
HanFeiZi hanFeiZi = new HanFeiZi();
//观察早餐
Watch watchBreakfast = new Watch(hanFeiZi,liSi,"breakfast");
//开始启动线程,监控
watchBreakfast.start();
//观察娱乐情况
Watch watchFun = new Watch(hanFeiZi,liSi,"fun");
watchFun.start();
//然后我们看看韩非子在干什么
Thread.sleep(1000); //主线程等待1秒后后再往下执行
hanFeiZi.haveBreakfast();
//韩非子娱乐了
Thread.sleep(1000);
hanFeiZi.haveFun();
}
}

运行结果如下所示:
韩非子:开始吃饭了…
李斯:观察到韩非子活动,开始向老板汇报了…
李斯:报告,秦老板!韩非子有活动了—>韩非子在吃饭
李斯:汇报完毕
韩非子:开始娱乐了…
李斯:观察到韩非子活动,开始向老板汇报了…
李斯:报告,秦老板!韩非子有活动了—>韩非子在娱乐
李斯:汇报完毕

按照如上的代码可以完成观察的需求。当通过一个while(true)的死循环来完全是不可取的,我们需要优化上述代码:
我们来想,既然韩非子一吃饭李斯就知道了,那我们为什么不把李斯这个类聚集到韩非子那个类上呢?

IHanFeiZi接口完全没有修改,我们来看实现类:

public class HanFeiZi implements IHanFeiZi{
//把李斯声明出来
private ILiSi liSi =new LiSi();
//韩非子要吃饭了
public void haveBreakfast(){
System.out.println("韩非子:开始吃饭了...");
//通知李斯
this.liSi.update("韩非子在吃饭");
}
//韩非子开始娱乐了
public void haveFun(){
System.out.println("韩非子:开始娱乐了...");
this.liSi.update("韩非子在娱乐");
}
}

韩非子HanFeiZi实现类就把接口的两个方法实现就可以了,在每个方法中调用
LiSi.update()方法,完成李斯观察韩非子的职责,李斯的接口和实现类都没有任何改变:我们再来看看Client程序的变更:

public class Client {
public static void main(String[] args) {
//定义出韩非子
HanFeiZi hanFeiZi = new HanFeiZi();
//然后我们看看韩非子在干什么
hanFeiZi.haveBreakfast();
//韩非子娱乐了
hanFeiZi.haveFun();
}
}

我们思考一下,修改后的程序运行结果正确,效率也比较高,是不是应该乐呵乐呵了?大功告成了?稍等等,你想在战国争雄的时候,韩非子这么有名望、有实力的人,就只有秦国关心他吗?想想也不可能呀,确实有一大帮的各国类似于李斯这样的人在看着他,监视着他的一举一动,但是看看我们的程序,你在HanFeiZi这个类中定义:

private ILiSi liSi =new LiSi();

这样一来只有李斯才能观察到韩非子,这是不对的,也就是说韩非子的活动只通知了李斯一个人,这不可能;再者说了,李斯只观察韩非子的吃饭、娱乐吗?政治倾向不关心吗?思维倾向不关心吗?杀人放火不关心吗?也就说韩非子的一系列活动都要通知李斯,这可怎么办?要按照上面的例子,我们如何修改?

我们在原来的基础上做了两个修改:
● 增加Observable
实现该接口的都是被观察者,那韩非子是被观察者,他当然也要实现该接口了,同时他
还有与其他庸人相异的事要做,因此他还是要实现IHanFeizi接口。
● 修改ILiSI接口名称为Observer
接口名称修改了一下,这样显得更抽象化,所有实现该接口的都是观察者(类似李斯这
样的)。

Observable是被观察者,就是类似韩非子这样的人,在Observable接口中有三个比较重要的方法,分别是addObserver增加观察者,deleteObserver删除观察者,notifyObservers通知所有的观察者,这是什么意思呢?我这里有一个信息,一个对象,我可以允许有多个对象来察看,你观察也成,我观察也成,只要是观察者就成,也就是说我的改变或动作执行,会通知其他的对象,看程序会更明白一点,先看Observable接口:

public interface Observable {
//增加一个观察者
public void addObserver(Observer observer);
//删除一个观察者
public void deleteObserver(Observer observer);
//既然要观察,我发生改变了他也应该有所动作,通知观察者
public void notifyObservers(String context);
}

被观察者实现类

public class HanFeiZi implements Observable ,IHanFeiZi{
//定义个变长数组,存放所有的观察者
private ArrayList<Observer> observerList = new ArrayList<Observer>();
//增加观察者
public void addObserver(Observer observer){
this.observerList.add(observer);
}
//删除观察者
public void deleteObserver(Observer observer){
this.observerList.remove(observer);
}
//通知所有的观察者
public void notifyObservers(String context){
for(Observer observer:observerList){
observer.update(context);
}
}
//韩非子要吃饭了
public void haveBreakfast(){
System.out.println("韩非子:开始吃饭了...");
//通知所有的观察者
this.notifyObservers("韩非子在吃饭");
}
//韩非子开始娱乐了
public void haveFun(){
System.out.println("韩非子:开始娱乐了...");
this.notifyObservers("韩非子在娱乐");
}
}

观察者只是把原有的ILiSi接口修改了一个名字而已,如代码清单22-11所示。

// 观察者接口
public interface Observer {
//一发现别人有动静,自己也要行动起来
public void update(String context);
}

具体的观察者1

public class LiSi implements Observer{
//首先李斯是个观察者,一旦韩非子有活动,他就知道,他就要向老板汇报
public void update(String str){
System.out.println("李斯:观察到韩非子活动,开始向老板汇报了...");
this.reportToQinShiHuang(str);
System.out.println("李斯:汇报完毕...\n");
}
//汇报给秦始皇
private void reportToQinShiHuang(String reportContext){
System.out.println("李斯:报告,秦老板!韩非子有活动了-->"+reportContext);
}
}

具体的观察者1

public class WangSi implements Observer{
//王斯,看到韩非子有活动
public void update(String str){
System.out.println("王斯:观察到韩非子活动,自己也开始活动了...");
this.cry(str);
System.out.println("王斯:哭死了...\n");
}
//一看韩非子有活动,他就痛哭
private void cry(String context){
System.out.println("王斯:因为"+context+",--所以我悲伤呀!");
}
}

具体的观察者2

public class LiuSi implements Observer{
//刘斯,观察到韩非子活动后,自己也得做一些事
public void update(String str){System.out.println("刘斯:观察到韩非子活动,开始动作了...");
this.happy(str);
System.out.println("刘斯:乐死了\n");
}
//一看韩非子有变化,他就快乐
private void happy(String context){
System.out.println("刘斯:因为" +context+",--所以我快乐呀!" );
}
}

场景类

public class Client {
public static void main(String[] args) {
//三个观察者产生出来
Observer liSi = new LiSi();
Observer wangSi = new WangSi();
Observer liuSi = new LiuSi();
//定义出韩非子
HanFeiZi hanFeiZi = new HanFeiZi();
//我们后人根据历史,描述这个场景,有三个人在观察韩非子
hanFeiZi.addObserver(liSi);
hanFeiZi.addObserver(wangSi);
hanFeiZi.addObserver(liuSi);
//然后这里我们看看韩非子在干什么
hanFeiZi.haveBreakfast();
}
}

运行结果如下所示:
韩非子:开始吃饭了…
李斯:观察到韩非子活动,开始向老板汇报了…
李斯:报告,秦老板!韩非子有活动了—>韩非子在吃饭
李斯:汇报完毕…
王斯:观察到韩非子活动,自己也开始活动了…
王斯:因为韩非子在吃饭——所以我悲伤呀!
王斯:哭死了…
刘斯:观察到韩非子活动,开始动作了…
刘斯:因为韩非子在吃饭——所以我快乐呀!刘斯:乐死了

好了,结果也正确了,也符合开闭原则了,同时也实现类间解耦,想再加观察者?继续实现Observer接口就成了,这时候必须修改Client程序,因为你的业务都发生了变化。这就是观察者模式。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
观察者模式是一种常用的设计模式,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象,当主题对象状态发生变化时,它的所有观察者都会收到通知并更新自己的状态。 在C++中,观察者模式通常由一个抽象主题类和多个具体观察者类组成。抽象主题类中定义了添加、删除和通知观察者的接口,具体观察者类实现了更新自身状态的方法。 以下是一个简单的观察者模式示例: ```c++ #include <iostream> #include <vector> class Observer { public: virtual void update() = 0; }; class Subject { public: void attach(Observer* observer) { observers.push_back(observer); } void detach(Observer* observer) { for (auto it = observers.begin(); it != observers.end(); ++it) { if (*it == observer) { observers.erase(it); break; } } } void notify() { for (auto observer : observers) { observer->update(); } } private: std::vector<Observer*> observers; }; class ConcreteObserver1 : public Observer { public: void update() override { std::cout << "ConcreteObserver1 updated" << std::endl; } }; class ConcreteObserver2 : public Observer { public: void update() override { std::cout << "ConcreteObserver2 updated" << std::endl; } }; int main() { Subject subject; ConcreteObserver1 observer1; ConcreteObserver2 observer2; subject.attach(&observer1); subject.attach(&observer2); subject.notify(); subject.detach(&observer1); subject.notify(); return 0; } ``` 在上面的示例中,Subject类是抽象主题类,Observer类是抽象观察者类,ConcreteObserver1和ConcreteObserver2是具体观察者类。当Subject对象状态发生变化时,它会通知所有观察者更新自己的状态。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值