Java的观察者模式

Posted 杯底囚人

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java的观察者模式相关的知识,希望对你有一定的参考价值。

在慕课网上学习了观察者模式,故做个笔记温习巩固一下http://www.imooc.com/learn/415

自己写一个经典观察者模式

观察者模式定义
定义:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖它的对象都得到了通知并自动更新。

好处:统一通知,各自处理。

认识观察者(六大方面):
1、目标与观察者之间的关系(1VS1、1VSn、nVS1)
2、单向依赖(又称为订阅发布模式)
3、命名建议
?1、目标接口的定义,建议在名称后跟Subject
?2、观察者接口的定义,建议在名称后跟Observer
?3、观察者接口的更新方法,建议命名为update,参数个数及类型不受限制。
4、触发通知的时机(完成状态维护后触发)
5、观察者模式的调用
6、通知的顺序(多个观察者之间的关系是平行的,不可以有依赖关系)

以下为一个经典的观察者模型。

package com.observer;

import com.subject.Subject;

/**
 * 这是一个观察者接口,定义一个更新的接口给那些在目标发生改变的时候被通知的对象
 * 
 * @author Administrator
 *
 */
public interface Observer {
    /**
     * 更新的接口
     * @param subject 传入目标对象,方便获取相应的目标对象的状态
     */
    public void update(Subject subject);

}
package com.observer;

import com.subject.ConcreteSubject;
import com.subject.Subject;

/**
 * 具体的观察者对象,实现更新的方法
 * @author Administrator
 *
 */
public class ConcreteObserver implements Observer {

    //观察者的状态
    private String observerState;

    /**
     * 获取目标类的状态同步到观察者的状态中
     */
    @Override
    public void update(Subject subject) {
        observerState = ((ConcreteSubject) subject).getSubjectState();

    }

}
package com.subject;

import java.util.ArrayList;
import java.util.List;

import com.observer.Observer;

/**
 * 目标对象,它知道观察它的观察者,并提供注册(添加)和删除观察者的接口
 * @author Administrator
 *
 */
public class Subject {
    //用来保存注册的观察者对象
    private List<Observer> observers = new ArrayList<Observer>();

    //attach detach notifyObservers
    public void attach(Observer observer){
        observers.add(observer);
    }

    /**
     * 删除集合中的指定观察者
     * @param observer
     */
    public void detach(Observer observer){
        observers.remove(observer);
    }

    /**
     * 通知所有注册的观察者对象
     */
    protected void notifyObservers(){
        for (Observer observer : observers) {
            observer.update(this);
        }
    }


}
package com.subject;
/**
 * 具体的目标对象,负责把有关状态存入到相应的观察者对象中
 * @author Administrator
 *
 */
public class ConcreteSubject extends Subject{

    //目标对象的状态
    private String subjectState;

    public String getSubjectState() {
        return subjectState;
    }

    public void setSubjectState(String subjectState) {
        this.subjectState = subjectState;
        this.notifyObservers();//通知
    }
}

推模型和拉模型

推模型:
?目标对象主动向观察者推送目标的详细信息
?推送的信息通常都是目标对象的全部或部分数据
拉模型:
?目标对象在通知观察者时,只传递少量信息
?如果观察者需要更具体的信息,由观察者主动到目标对象中获取,相当于是观察者从目标对象中拉数据
?一般这种模型的实现,会把目标对象通过update方法传递给观察者

比较:
?推模型是假定目标对象知道观察者需要的数据
?拉模型是目标对象不知道观察者具体需要什么数据,因此把自身传给观察者,由观察者来取值
?推模型会使观察者对象难以复用
?拉模型下,update方法的参数是目标对象本身,基本上可以适应各种情况的需要。

一下是经典的观察者模型的一个实用的案例

package com.observer;

import com.subject.WeatherSubject;

/**
 * 这是一个观察者接口,定义一个更新的接口给那些在目标发生改变的时候被通知的对象
 * 
 * @author Administrator
 *
 */
public interface Observer {
    /**
     * 更新的接口
     * @param subject 传入目标对象,方便获取相应的目标对象的状态
     */
    public void update(WeatherSubject subject);
}
package com.observer;

import com.subject.ConcreteWeatherSubject;
import com.subject.WeatherSubject;

/**
 * 具体的观察者对象,实现更新的方法
 * 
 * @author Administrator
 *
 */
public class ConcreteObserver implements Observer {

    // 观察者的名字,是谁收到了这个讯息
    private String observerName;

    // 天气内容的情况,这个消息从目标出获取
    private String weatherContent;

    // 提醒的内容
    private String remindThing;

    public String getObserverName() {
        return observerName;
    }

    public void setObserverName(String observerName) {
        this.observerName = observerName;
    }

    public String getWeatherContent() {
        return weatherContent;
    }

    public void setWeatherContent(String weatherContent) {
        this.weatherContent = weatherContent;
    }

    public String getRemindThing() {
        return remindThing;
    }

    public void setRemindThing(String remindThing) {
        this.remindThing = remindThing;
    }

    /**
     * 获取目标类的状态同步到观察者的状态中
     */
    @Override
    public void update(WeatherSubject subject) {
         weatherContent = ((ConcreteWeatherSubject) subject).getWearherContent();
        System.out.println(observerName + "收到了" + weatherContent + "," + remindThing);

    }

}
package com.subject;

import java.util.ArrayList;
import java.util.List;

import com.observer.Observer;

/**
 * 目标对象,它知道观察它的观察者,并提供注册(添加)和删除观察者的接口
 * @author Administrator
 *
 */
public class WeatherSubject {
    //用来保存注册的观察者对象
    private List<Observer> observers = new ArrayList<Observer>();

    //attach detach notifyObservers
    /**
     * 把订阅天气的人添加到观察者列表
     * @param observer
     */
    public void attach(Observer observer){
        observers.add(observer);
    }

    /**
     * 删除集合中的指定订阅天气的人
     * @param observer
     */
    public void detach(Observer observer){
        observers.remove(observer);
    }

    /**
     * 通知所有已经订阅天气的人
     */
    protected void notifyObservers(){
        for (Observer observer : observers) {
            observer.update(this);
        }
    }


}
package com.subject;
/**
 * 具体的目标对象,负责把有关状态存入到相应的观察者对象中
 * @author Administrator
 *
 */
public class ConcreteWeatherSubject extends WeatherSubject{

    //获取天气的内容信息
    private String wearherContent;

    public String getWearherContent() {
        return wearherContent;
    }

    public void setWearherContent(String wearherContent) {
        this.wearherContent = wearherContent;
        //内容有了,说明天气更新了,通知所有订阅的人
        this.notifyObservers();
    }


}
package com.test;

import com.observer.ConcreteObserver;
import com.subject.ConcreteWeatherSubject;

public class Client {
    public static void main(String[] args) {

        //1、创建目标
        ConcreteWeatherSubject weather = new ConcreteWeatherSubject();

        //2、创建观察者
        ConcreteObserver observerGirl = new ConcreteObserver();
        observerGirl.setObserverName("美女");
        observerGirl.setRemindThing("约吗?");
        ConcreteObserver observerGirl2 = new ConcreteObserver();
        observerGirl2.setObserverName("美女2号");
        observerGirl2.setRemindThing("约不约?");

        //3、注册观察者
        weather.attach(observerGirl);
        weather.attach(observerGirl2);

        //4、目标发布天气
        weather.setWearherContent("明天天气晴朗,蓝天白云");

    }
}

Java自身也提供了观察者模式

java.util包下的Observable类中含有观察者模式的大部分方法,Observer接口中含有update方法

java实现观察者模式与自身实现的对比:
1、不需要再定义观察者和目标的接口了,JDK帮忙定义了。
2、具体的目标实现里面不需要再维护观察者的注册信息了,这个在Java的Observable类里面已经帮忙实现好了。
3、触发通知的方法有一点变化,需要调用setChanged方法,这个是java为了帮助实现更精确的触发控制而提供的功能
4、具体的观察者实现里面,update方法其实能同时支持推模型和拉模型。这是java在定义的时候,就已经考虑进去了

观察者模式的有点:
1、观察者模式实现了观察者和目标之间的抽象耦合
2、观察者模式实现了动态联动
3、观察者模式支持广播通信

观察者模式的缺点:可能会引起无畏的操作。

以下是使用java自带的观察者模式的一个案例

import java.util.Observable;

//天气目标的具体实现类
public class ConcreteWeatherSubject extends Observable {

    //天气情况的内容
    private String content;

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
        //天气情况有了要通知所有的观察者
        //注意在通知之前,在调用Java中的Observer模式时,下面这句话必不可少
        this.setChanged();
        //主动通知,这里使用的是推模型
        this.notifyObservers(content);
        /*
         * 拉方法,不带参数
         * this.notifyObservers();
         */
    }


}
import java.util.Observable;
import java.util.Observer;

//具体的观察者对象
public class ConcreteObserver implements Observer {

    // 定义观察者名称的对象
    private String observerName;

    @Override
    public void update(Observable o, Object arg) {
        //推模式
        System.out.println(observerName+"收到了消息,目标推送过来的是:"+arg);
        //拉模式
        System.out.println(observerName+"收到了消息,主动去目标拉取过来的是:"+((ConcreteWeatherSubject)o).getContent());

    }

    public String getObserverName() {
        return observerName;
    }

    public void setObserverName(String observerName) {
        this.observerName = observerName;
    }

}

public class Client {
    public static void main(String[] args) {
        //创建天气作为一个目标,也可以说是被观察者
        ConcreteWeatherSubject subject = new ConcreteWeatherSubject();
        //创建具体的观察者
        ConcreteObserver girl = new ConcreteObserver();
        girl.setObserverName("美女");

        //创建具体的观察者
        ConcreteObserver girl2 = new ConcreteObserver();
        girl2.setObserverName("美女2号");

        //注册观察者
        subject.addObserver(girl);
        subject.addObserver(girl2);

        //目标更新天气情况了。
        subject.setContent("天气更新了。");
    }
}

区别对待的观察者模式

观察者模式适用情况:
1、当一个抽象模型有两个方面,其中一个方面的操作依赖于两一个方面的状态变化
2、更改一个对象的时候,需要同时连带改变其他的对象,而且不知道究竟需要有多少对象需要被连带改变
3、当一个对象必须通知其他的对象,但是你又希望这个对象和其他被通知的对象是松散耦合的。

在以上的案例中,加入不同的观察者希望接收目标的天气预报,而非目标内的天气预报不被接收。可以采用以下的案例。

package com.observer;

import com.subject.WeatherSubject;

public interface Observer {

    //更新的接口
    public void update(WeatherSubject subject);

    //设置观察者名称
    public void setObserverName(String observerName);

    //取得观察者名称
    public String getObserverName();

}
package com.observer;

import com.subject.ConcreteWeatherSubject;
import com.subject.WeatherSubject;

public class ConcreteObserver implements Observer {

    // 观察者的名称
    private String observerName;

    // 天气情况的内容
    private String weatherContent;

    // 提醒的内容
    private String remindThing;

    @Override
    public void update(WeatherSubject subject) {

        weatherContent = ((ConcreteWeatherSubject)subject).getWeatherContent();
        System.out.println(observerName+"收到了"+weatherContent+","+remindThing);

    }

    @Override
    public void setObserverName(String observerName) {
        this.observerName = observerName;
    }

    @Override
    public String getObserverName() {
        return observerName;
    }

    public String getWeatherContent() {
        return weatherContent;
    }

    public void setWeatherContent(String weatherContent) {
        this.weatherContent = weatherContent;
    }

    public String getRemindThing() {
        return remindThing;
    }

    public void setRemindThing(String remindThing) {
        this.remindThing = remindThing;
    }

}
package com.subject;

import java.util.ArrayList;
import java.util.List;

import com.observer.Observer;

public abstract class WeatherSubject {

    //用来保存注册的观察者对象
    public List<Observer> observers = new ArrayList<Observer>();

    public void attach(Observer observer){
        observers.add(observer);
    }

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

    protected abstract void notifyObserver();

}
package com.subject;

import com.observer.Observer;

public class ConcreteWeatherSubject extends WeatherSubject {

    // 晴天、下雨、下雪
    // 目标对象的状态
    private String weatherContent;

    @Override
    protected void notifyObserver() {
        // 循环所有注册的观察者
        for (Observer observer : observers) {
            // 规则是:
            // 黄明的女朋友需要“下雨”的条件
            // 黄明的老妈需要“下雪”或者“下雨”的条件

            // 如果天气是晴天
            // do nothing

            // 如果天气是下雨
            if ("下雨".equals(this.getWeatherContent())) {
                if ("黄明的女朋友".equals(observer.getObserverName())) {
                    observer.update(this);
                }
                if ("黄明的老妈".equals(observer.getObserverName())) {
                    observer.update(this);
                }
            }
            // 如果天气是下雪
            if ("下雪".equals(this.getWeatherContent())) {
                if ("黄明的老妈".equals(observer.getObserverName())) {
                    observer.update(this);
                }
            }
        }

    }

    public String getWeatherContent() {
        return weatherContent;
    }

    public void setWeatherContent(String weatherContent) {
        this.weatherContent = weatherContent;
        this.notifyObserver();

    }

}
package com.test;

import com.observer.ConcreteObserver;
import com.subject.ConcreteWeatherSubject;

public class Client {
    public static void main(String[] args) {
        //1、创建目标
        ConcreteWeatherSubject weatherSubject = new ConcreteWeatherSubject();
        //2、创建观察者
        ConcreteObserver observerGirl = new ConcreteObserver();
        observerGirl.setObserverName("黄明的女朋友");
        observerGirl.setRemindThing("下雨了,安静的呆在家里吧");

        ConcreteObserver observerMum = new ConcreteObserver();
        observerMum.setObserverName("黄明的老妈");
        observerMum.setRemindThing("不管下雨还是下雪,都不出门了");

        //3、注册观察者
        weatherSubject.attach(observerGirl);
        weatherSubject.attach(observerMum);

        //4、目标发布天气
        weatherSubject.setWeatherContent("下雨");
        System.out.println("+++++++++++++++++");
        weatherSubject.setWeatherContent("下雪");
        System.out.println("+++++++++++++++++");
        weatherSubject.setWeatherContent("晴天");
    }
}

以上是关于Java的观察者模式的主要内容,如果未能解决你的问题,请参考以下文章

设计模式之单例模式

观察者模式/ java实现附代码 /

[Android&amp;Java]浅谈设计模式-代码篇:观察者模式Observer

java观察者模式代码,含答案解析

Java设计模式-观察者模式

Java设计模式-观察者模式