设计模式-观察者模式

时间:2024-01-21 15:10:46

1、定义

  定义对象的一种一对多/一的依赖关系。当一个对象的状态发生改变时,所有依赖它的对象都得到通知并被自动更新。

2、实现观察者模式

观察者接口:

package com.cn.shejimoshi.guanchazhemoshi;

public interface Observer {

    void update(WeatherSubject weatherSubject);
}

观察者实现类:

package com.cn.shejimoshi.guanchazhemoshi;

public class ConcreteObserver implements Observer {

    private String observerName;//观察者名称

    public ConcreteObserver(String observerName) {
        this.observerName = observerName;
    }

    /**
     * 各个观察者被通知,是通过在主题中调用该观察者的方法实现的
     * @param weatherSubject
     */
    public void update(WeatherSubject weatherSubject) {
        System.out.println("观察者 "+observerName+" 获取的信息是:"
                +((ConcreteWeatherSubject)weatherSubject).getWeatherContext());
    }
}

主题:

package com.cn.shejimoshi.guanchazhemoshi;

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

public class WeatherSubject {

    List<Observer> observers=new ArrayList<Observer>();//维护观察者的集合

    public void addObserver(Observer observer){//添加观察者到集合
        observers.add(observer);
    }

    public void notifyObservers(){//通知各个观察者--实际使用观察者对象调用观察者的update方法
        for (Observer observer: observers){
            observer.update(this);
        }
    }

    public List<Observer> getObservers() {
        return observers;
    }

    public void setObservers(List<Observer> observers) {
        this.observers = observers;
    }

}

主题子类:

package com.cn.shejimoshi.guanchazhemoshi;

public class ConcreteWeatherSubject extends WeatherSubject {

    private String weatherContext;

    public void setWeatherContext(String weatherContext) {//主题中的weatherContext发生变化,则去通知观察者
        this.weatherContext = weatherContext;
        this.notifyObservers();
    }

    public String getWeatherContext() {
        return weatherContext;
    }
}

测试方法:

public class WeatherSubjectTest {

    @Test
    public void test(){
     //创建观察者
        Observer observer1=new ConcreteObserver("小明");
        Observer observer2=new ConcreteObserver("小花");
     //创建主题
        ConcreteWeatherSubject weatherSubject=new ConcreteWeatherSubject();
     //在主题中注册观察者 weatherSubject.addObserver(observer1); weatherSubject.addObserver(observer2);      //主题变化内容 weatherSubject.setWeatherContext(
"下雨天留客天天留我不留"); } }

  说明:上述实例中,观察者的update方法参数为主题的实例,该方式是观察者通过获取主题的实例,从而获取主题信息实现,称为拉模型(观察者通过获取主题的引用去拉取数据);如果update方法的参数为具体的数据,如改成String类型的数据,然后在主题中调用时传入weatherContext,称为推模型(主题具体信息推给观察者)

3、使用java提供的观察者实现

  • 不需要定义主题父类、不需要定义观察者接口,JDK已提供
  • 主题不需要维护观察者,JDK已提供
  • 主题在通知必须调用setChanged(),否则通知不起作用

观察者实现:

 

package com.cn.shejimoshi.guanchazhemoshi2;

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

public class ConcreteObserver implements Observer {

    private String observerName;

    ConcreteObserver(String observerName){
       this.observerName=observerName;
    }

    //既可以支持推模式,也可以支付拉模式。如果主题使用推模式,则同时支持;如果主题使用拉模式,则只支持拉模式(即arg为空)
    public void update(Observable o, Object arg) {
        System.out.println("观察者"+observerName+" 具体类信息:"+o);
        System.out.println("观察者"+observerName+" 参数信息:"+arg);
    }
}

 

主题子类:

package com.cn.shejimoshi.guanchazhemoshi2;

import java.util.Observable;

public class ConcreteSubject extends Observable {

    private String context;
    private int number;
    public String getContext() {
        return context;
    }
    public void setNumber(int number) {
        this.number = number;
    }

    public void setContext(String context) {
        this.context = context;
        this.setChanged();
        this.notifyObservers(context); //推模式
//      this.notifyObservers();//拉模式
    }

    @Override
    public String toString() {
        return "ConcreteSubject{" +
                "context='" + context + '\'' +
                ", number=" + number +
                '}';
    }
}

测试方法:

public class ConcreteSubjectTest {

    @Test
    public void test(){

        ConcreteObserver concreteObserver1=new ConcreteObserver("大哈");
        ConcreteObserver concreteObserver2=new ConcreteObserver("大牛");

        ConcreteSubject concreteSubject=new ConcreteSubject();
        concreteSubject.addObserver(concreteObserver1);
        concreteSubject.addObserver(concreteObserver2);

        concreteSubject.setNumber(121);
        concreteSubject.setContext("我在世界之巅");
    }
}