【转】生活中的观察者模式——一壶水的故事

生活中的观察者模式——一壶水的故事

观察者模式,顾名思意就是观察与被观察的关系,比如你在烧开水得时时看着它开没开,你就是观察者,开水就是被观察者;再比如说你在带小孩,你关注她是不是饿了,是不是喝了,是不是撒尿了,你就是观察者,小孩就被观察者。观察者模式是对象的行为模式,又叫发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。当你看这些模式的时候,不要觉得陌生,它们就是观察者模式。

烧开水

观察者模式一般是一种一对多的关系,可以有任意个(一个或多个)观察者对象同时监听某一个对象。监听的对象叫观察者(后面提到监听者,其实就指观察者,两者是等价的),被监听的对象叫被观察者(Observable,也叫主题Subject)。被观察者对象在状态上发生变化时,会通知所有观察者对象,使它们能够做出相应的变化(如自动更新自己的信息)。

我们就以上面提到的烧开水的一个简单生活实例来模拟一下观察者模式。

版本一:ObserverModule.java

//人,观察者
class Person {
    public void update(String data) {
        System.out.println(data + "关电源...");
    }
}

//水,被观察者
class Water {
    private Person person;
    private boolean isBoiled;
    public Water() {
        isBoiled = false;
    }

    public void SetBoiled() {
        isBoiled = true;
        notifyObserve();
    }
    public void addObserver(Person person) {
        this.person = person;
    }

    public void removeObserver() {
        if (person != null) {
            person = null;
        }
    }

    public void notifyObserve() {
        if (isBoiled && person != null) {
            person.update("水开了,");
            isBoiled = false;
        }
    }
}

//客户端
public class ObserverModule {
    public static void main(String args[]) {
        Person person = new Person();
        Water water = new Water();
        water.addObserver(person);
        water.SetBoiled();
    }
} 

结果如下:

水开了,关电源…

这个代码非常简单,水开了就会通知人,人就去关电源。但也有一个问题,就是拓展性不好,不灵活。如果我们烧的开水不是用来喝,而用来洗澡,我就要监测它的温度,可能50度就关电源,也可能要60度才行,这样一个监听就不够了,还监听温度的随时变化;再比如水开了之后,我不是关电源,而是让它保温。你的updae又得改了……

所以上面这个代码拓展行是不好,但已经实现了我们的基本想法,这算是我们的第一个版本(版本)。接下来我们再看一下,升级版:

版本2:ObserverModule.java

//观察者
interface Observer {
    public void update(Observable observable);
}

//被观察者
abstract class  Observable {
    protected boolean isChanaged;
    protected List<Observer> observers = new ArrayList<Observer>();

    public Observable() {
        isChanaged = false;
    }
    public void addObserver(Observer observer) {
        observers.add(observer);
    }

    public void removeObserver(Observer observer) {
        observers.remove(observer);
    }

    public void removeObservers() {
        observers.clear();
    }
    public void notifyObservers() {
        if (isChanaged) {
            for (int i = 0; i < observers.size(); i ++) {
                observers.get(i).update(this);
            }
            isChanaged = false;
        }
    }
}

//人,温度监测
class TemperatureObserver implements Observer{
    @Override
    public void update(Observable observable) {
        Water water = (Water)observable;
        System.out.println("温度:" + water.getTemperature() +  "    状态:" + water.getStatus());
        System.out.println("TemperatureObserver observing...");
    }
}

class BoildObserver implements Observer {
    String doSomthing;
    BoildObserver(String doSomthing) {
        this.doSomthing = doSomthing;
    }

    @Override
    public void update(Observable observable) {
        Water water = (Water)observable;
        if (water.getTemperature() >= 100) {
            System.out.println("状态:" + water.getStatus());
            System.out.println("BoildObserver:" + doSomthing);
        }

    }
}
//水,被观察者
class Water extends Observable{
    private double temperature;
    private String status;

    public Water() {
        super();
        this.temperature = 0;
        this.status = "冷水";
    }

    public Water(Observer observer) {
        this();
        observers.add(observer);
    }

    public double getTemperature() {
        return temperature;
    }

    public String getStatus() {
        return status;
    }

    public void change(double temperature) {
        this.temperature = temperature;
        if (temperature < 40) {
            status = "冷水";
        } else if (temperature >= 40 && temperature < 60) {
            status = "温水";
        }else if (temperature >= 60 && temperature < 100 ) {
            status = "热水";
        } else {
            status = "开水";
        }
        this.isChanaged = true;
        notifyObservers();
    }
}

//客户端
public class ObserverModule {
    public static void main(String args[]) {
        TemperatureObserver temperatureObserver = new TemperatureObserver();
        BoildObserver boildObserver1 = new BoildObserver("关闭电源...");
        BoildObserver boildObserver2 = new BoildObserver("继续保湿...");
        Water water = new Water(temperatureObserver);
        water.addObserver(boildObserver1);
        water.addObserver(boildObserver2);
        water.change(45);
        water.change(80);
        water.change(100);
    }
} 

结果如下:

温度:45.0 状态:温水

TemperatureObserver observing…

温度:80.0 状态:热水

TemperatureObserver observing…

温度:100.0 状态:开水

TemperatureObserver observing…

状态:开水

BoildObserver:关闭电源…

状态:开水

BoildObserver:继续保湿…

观察者模式设计

通过上面这个活生生的例子,我们总结一下观察者模式的设计。
观察者模式的类结构关系如下:

观察者模式的类图结构

在设计观察者模式的程序时要注意以下几点:

1. 要明确谁是观察者谁是被观察者,只要明白谁是关注对象,问题也就明白了。一般观察者与被观察者之间的是多对一的关系,一个被观察对象可以有多个监听对象(观察者)。如一个编辑框,有鼠标点击的监听者,也有键盘的监听者,还有内容改变的监听者。

2. Observable在发送广播通知的时候,无须指定具体的Observer,Observer可以自己决定是否要订阅Subject的通知。

3. 被观察者至少需要有三个方法:添加监听者、移除监听者、通知Observer的方法;观察者至少要有一个方法:更新方法,更新当前的内容,作出相应的处理。 注:添加监听者、移除监听者在不同的模型中可能会有不同命名,如观察者模型中一般,addObserver、removeObserver;在源-监听器(Source/Listener)模型中一般是attach/detach,应用在桌面编程的窗口中,还可能是attachWindow/detachWindow,或Register/UnRegister。不要被名称迷糊了,不管他们是什么名称,其实功能都是一样的,就是添加/删除观察者。

4. 观察者模式的应用场景: <1>.对一个对象状态的更新需要其他对象同步更新;,或者一个对象的更新需要依赖另一个对象的更新;<2>.对象仅需要将自己的更新通知给其他对象而不需要知道其他对象的细节,如消息推送。

推模型和拉模型

观察者模式根据其侧重的功能还可以分为推模型和拉模式

推模型:被观察者对象向观察者推送主题的详细信息,不管观察者是否需要,推送的信息通常是主题对象的全部或部分数据。一般这种模型的实现中,会把被观察者对象中的全部或部分信息通过update的参数传递给观察者[update(Object obj) ]。

拉模型:被观察者在通知观察者的时候,只传递少量信息。如果观察者需要更具体的信息,由观察者主动到被观察者对象中获取,相当于是观察者从被观察者对象中拉数据。一般这种模型的实现中,会把被观察者对象自身通过update方法传递给观察者[update(Observable observable ) ],这样在观察者需要获取数据的时候,就可以通过这个引用来获取了。

观察者模式还可以用于网络中的客户端和服务器,比如手机中的各种App的消息推送,服务端是被观察者,各个手机App是观察者,一旦服务器上的数据(如App升级信息)有变更,就会被推送到手机客户端。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值