观察者模式(Observer)

179 阅读4分钟

1. 问题场景

当一个对象的状态放生改变的时候,如何让依赖于它的所有对象得到通知,并进行相应的处理?

2. UML图

这里写图片描述

Subject:目标对象,通常具有以下功能

  • 一个目标对象可以被多个观察者观察
  • 目标提供观察者注册和退订的维护
  • 当目标状态发生改变时,目标负责通知所有注册的、有效的观察者

Observer:定义观察者接口,提供目标通知时对应的更新方法,这个更新方法进行相应的业务逻辑处理,可以在这个方法回调目标对象,以获取目标对象的数据

ConcreteSubject:具体的目标对象,用来维护目标的状态,当目标状态发生改变时,通知所有注册有效的观察者,让观察者执行相应的处理

ConcreteObserver:观察者的具体对象,用来接收目标的通知,并进行相应的后续处理

3. 具体代码实现代码:

// Subject:
public class Subject {
    //注意:Arraylist里面可以添加null元素
    private List<Observer> readers = new ArrayList<>();

    public void attach(Observer reader){
        if(reader != null){
            readers.add(reader);
        }
    }

    public void detach(Observer reader){
        if(reader != null){
            readers.remove(reader);
        }
    }

    public void notifyAllReaders(){
        if(readers.size() != 0){
            //通过流的方式来访问
            //因为是从Newapaper方法里调用,所有this表示Newspaper实例
            readers.forEach(reader -> reader.update(this));
        }

    }
}

//Newspaper:
public class Newspaper extends  Subject{
    private String content;
    public String getContent(){
        return content;
    }
    //维护目标的状态
    public void setContent(String content){
        //内容更新之后通知所有观察者
        this.content = content;
        notifyAllReaders();
    }
}

// Observer:
public interface Observer {
    public void update(Subject subject);
}

// Reader:
public class Reader implements Observer {
   
    private String name;

    public void setName(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }

    @Override
    public void update(Subject subject) {
        System.out.println(name + "收到了报纸\n 报纸的内容为: " 
                                         + ((Newspaper)subject).getContent() );
    }
}


//Client端
public class Client {
    public static void main(String[] args) {
       //创建一个报纸,作为被观察者
       NewsPaper subject = new NewsPaper();
       //创建阅读者,也就是观察者
       Reader reader1 = new Reader();
       reader1.setName("张三");
 
       Reader reader2 = new Reader();
       reader2.setName("李四");
 
       Reader reader3 = new Reader();
       reader3.setName("王五");
 
       //注册阅读者
       subject.attach(reader1);
       subject.attach(reader2);
       subject.attach(reader3);
 
       //要出报纸啦
       subject.setContent("本期内容是观察者模式");
    }
}

4. 研磨设计模式

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

观察者模式把多个订阅者称为观察者Observer,多个观察者观察的对象的被称为目标Subject。

一个目标可以有任意多个观察者对象,一旦目标的状态发生改变时,所有注册的观察者就会得到通知,然后各个观察者会对通知作出相应的处理,执行相应的业务功能处理,并使自己的状态和目标对象的状态保持一致。

观察者模式的本质:触发联动

Swing中的观察者模式:Swing组件是被观察的目标,而每个实现监听的类就是观察者,监听器的接口就是观察者的接口,在调用addXXXListener方法的时候就相当于注册观察者。当组件被单击时,状态发生改变的时候,就会产生相应的通知,会调用注册的观察者的方法,就是我们所实现的监听器的方法。

扩展:区别对待观察者

/**
 * 定义水质监测的目标对象
 */
public abstract class WaterQualitySubject {
	/**
	 * 用来保存注册的观察者对象
	 */
	protected List<WatcherObserver> observers = new ArrayList<WatcherObserver>();
	/**
	 * 注册观察者对象
	 * @param observer 观察者对象
	 */
	public void attach(WatcherObserver observer) {
		observers.add(observer);
	}
	/**
	 * 删除观察者对象
	 * @param observer 观察者对象
	 */
	public void detach(WatcherObserver observer) {
		observers.remove(observer);
	}
	/**
	 * 通知相应的观察者对象
	 */
	public abstract void notifyWatchers();
	/**
	 * 获取水质污染的级别
	 * @return 水质污染的级别
	 */
	public abstract int getPolluteLevel();
}

/**
 * 具体的水质监测对象
 */
public class WaterQuality extends WaterQualitySubject{
	/**
	 * 污染的级别,0表示正常,1表示轻度污染,2表示中度污染,3表示高度污染
	 */
	private int polluteLevel = 0;
	/**
	 * 获取水质污染的级别
	 * @return 水质污染的级别
	 */
	public int getPolluteLevel() {
		return polluteLevel;
	}
	/**
	 * 当监测水质情况后,设置水质污染的级别
	 * @param polluteLevel 水质污染的级别
	 */
	public void setPolluteLevel(int polluteLevel) {
		this.polluteLevel = polluteLevel;
		//通知相应的观察者
		this.notifyWatchers();
	}
	/**
	 * 通知相应的观察者对象
	 */
	public void notifyWatchers() {
		//循环所有注册的观察者
		for(WatcherObserver watcher : observers){
						//开始根据污染级别判断是否需要通知,由这里总控
						if(this.polluteLevel >= 0){
							//通知监测员做记录
							if("监测人员".equals(watcher.getJob())){
								watcher.update(this);
							}
						}
						if(this.polluteLevel >= 1){
							//通知预警人员
							if("预警人员".equals(watcher.getJob())){
								watcher.update(this);
							}
						}
						if(this.polluteLevel >= 2){
							//通知监测部门领导
						   if("监测部门领导".equals(watcher.getJob())){
								watcher.update(this);
							}
						}
		}
	}
}

/**
 * 水质观察者接口定义
 */
public interface WatcherObserver {
	/**
	 * 被通知的方法
	 * @param subject 传入被观察的目标对象
	 */
	public void update(WaterQualitySubject subject);
	/**
	 * 设置观察人员的职务
	 * @param job 观察人员的职务
	 */
	public void setJob(String job);
	/**
	 * 获取观察人员的职务
	 * @return 观察人员的职务
	 */
	public String getJob();
}

/**
 * 具体的观察者实现
 */
public class Watcher implements WatcherObserver{
	/**
	 * 职务
	 */
	private String job;
	
	public void update(WaterQualitySubject subject) {
		//这里采用的是拉的方式
		System.out.println(job+"获取到通知,当前污染级别为:"+subject.getPolluteLevel());
	}
	
	public String getJob() {
		return this.job;
	}
	
	public void setJob(String job) {
		this.job = job;
	}
}

public class Client {
	public static void main(String[] args) {
		//创建水质主题对象
		WaterQuality subject = new WaterQuality();
		//创建几个观察者
		WatcherObserver watcher1 = new Watcher();
		watcher1.setJob("监测人员");
		WatcherObserver watcher2 = new Watcher();
		watcher2.setJob("预警人员");
		WatcherObserver watcher3 = new Watcher();
		watcher3.setJob("监测部门领导");
		//注册观察者
		subject.attach(watcher1);
		subject.attach(watcher2);
		subject.attach(watcher3);
		
		//填写水质报告
		System.out.println("当水质为正常的时候------------------〉");
		subject.setPolluteLevel(0);
		System.out.println("当水质为轻度污染的时候---------------〉");
		subject.setPolluteLevel(1);
		System.out.println("当水质为中度污染的时候---------------〉");
		subject.setPolluteLevel(2);
	}
}