Java设计模式之观察者模式/发布订阅模式(Observer)

1. 观察者模式

1.1 定义

观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,是它们能够自动更新自己。(引自《大话设计模式》)

1.2 解释

观察者模式,又称为发布订阅模式(Publish/Subscribe),顾名思义,发布者发布信息,订阅者接收信息,订阅了就能收到信息,没订阅就不能收到信息。

1.3 结构图

Java设计模式之观察者模式/发布订阅模式(Observer)_第1张图片

相关说明:

  • Subject类:抽象主题或称为抽象通知者,它把所有对观察者对象的引用保存在一个集合里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象。
  • ConcreteSubject类:具体主题,将有关状态存入具体观察者对象。在具体主题的内部状态改变时,给所有登记过的观察者发出通知。
  • Observer类:抽象观察者,为所有的具体观察者定义一个接口,在得到主题的通知时更新自己。
  • ConcreteObserver类:具体观察者,实现抽象观察者角色所要求的更新接口,并根据主题的状态更新自己的状态,以保持一致。

1.4 基本代码

Subject类

/**
 * 抽象主题类(或称为抽象通知类)
 */
public interface Subject {
	/**
	 * 注册观察者
	 * @param observer
	 */
	public void registerObserver(Observer observer);
	/**
	 * 删除观察者
	 * @param observer
	 */
	public void removeObserver(Observer observer);
	/**
	 * 通知所有注册的观察者
	 */
	public void notifyObserver();
	
}

Observer类

/**
 * 抽象观察者类
 * 当主题类调用notifyObserver()方法时,update()方法会被回调
 */
public interface Observer {
	/**
	 * 更新方法
	 */
	public void update();

}

ConcreteSubject具体实现类

/**
 * 具体主题类(或称为具体通知类)
 */
public class ConcreteSubject implements Subject {
	//存储观察者:注意此处List的参数为抽象类Observer,设计原则:面向接口编程,而不是面向实现编程
	private List observers = new ArrayList();
	//主题类的状态
	private String subjectState;

	@Override
	public void registerObserver(Observer observer) {
		observers.add(observer);
	}

	@Override
	public void removeObserver(Observer observer) {
		if(!observers.isEmpty() && observers.contains(observer)) {
			observers.remove(observer);
		}
	}

	@Override
	public void notifyObserver() {
		for (Observer observer : observers) {
			observer.update();
		}
		
	}

	public String getSubjectState() {
		return subjectState;
	}

	public void setSubjectState(String subjectState) {
		this.subjectState = subjectState;
	}
	
	
}

ConcreteObserver具体实现类

public class ConcreteObserver implements Observer {

	private String name;
	private String ObserverMessage;
	private ConcreteSubject subject;
	
	public ConcreteObserver(String name, ConcreteSubject subject) {
		this.name = name;
		this.subject = subject;
	}
	
	@Override
	public void update() {
		this.ObserverMessage =  subject.getSubjectState();
		System.out.println(this.name+"更新状态为:"+this.ObserverMessage);
	}

	public ConcreteSubject getSubject() {
		return subject;
	}

	public void setSubject(ConcreteSubject subject) {
		this.subject = subject;
	}

}

测试类

public class ObserverTest {

	public static void main(String[] args) {
		ConcreteSubject subject = new ConcreteSubject();
		ConcreteObserver observerA = new ConcreteObserver("A",subject);
		ConcreteObserver observerB = new ConcreteObserver("B",subject);
		ConcreteObserver observerC = new ConcreteObserver("C",subject);
		
		subject.registerObserver(observerA);
		subject.registerObserver(observerB);
		subject.registerObserver(observerC);
		
		subject.setSubjectState("X");
		subject.notifyObserver();
		
		System.out.println("--------------华丽丽的分割线----------------");
		//移除B
		subject.removeObserver(observerB);
		subject.setSubjectState("Y");
		subject.notifyObserver();
	}

}

测试结果:

Java设计模式之观察者模式/发布订阅模式(Observer)_第2张图片

1.5 说明

  • subject类,可以是接口也可以是抽象类,当多个子类对父类方法有相同的处理过程时,就可以用抽象类,将共同的部分抽象到父类中。
  • ConcreteObserver类,当subject的状态发生改变时,观察者相应的更新自己的状态,以保持和subject的状态保持一致。
  • 测试类中,第一次注册了三个观察者,所以通知三个,第二次Y取消了注册,所以只通知了X、Z。

2. 实战应用

2.1 举个栗子

一个学校,有一些事情,需要通知该学校所有的学生。

这里的学校就相当于Subject通知者,学生就相当于Observer观察者。

2.2 代码实现

Subject类,这个类不变,此处采用接口。

/**
 * 抽象主题类(或称为抽象通知类)
 */
public interface Subject {
	/**
	 * 注册观察者
	 * @param observer
	 */
	public void registerObserver(Observer observer);
	/**
	 * 删除观察者
	 * @param observer
	 */
	public void removeObserver(Observer observer);
	/**
	 * 通知所有注册的观察者
	 */
	public void notifyObserver();
	
}

Observer类,这个类也不变,主要方法就是更新,当学校有事通知(即subject状态改变)时,调用此方法。

/**
 * 抽象观察者类
 */
public interface Observer {
	/**
	 * 更新方法
	 */
	public void update(String message);

}

School类,具体主题实现类,此处为校方,继承Subject类,实现Subject中的方法。声明了List集合,对象为观察者,当自身状态(schoolMessage)发生改变时,遍历list通知所有注册的观察者(学生)。

/**
 * 具体主题类(或称为具体通知类)
 */
public class School implements Subject {
	//存储观察者
	private List observers;
	//主题类的状态
	private String schoolMessage;

	public School() {
		observers = new ArrayList();
	}
	
	@Override
	public void registerObserver(Observer observer) {
		observers.add(observer);
	}

	@Override
	public void removeObserver(Observer observer) {
		if(!observers.isEmpty() && observers.contains(observer)) {
			observers.remove(observer);
		}
	}

	@Override
	public void notifyObserver() {
		for (Observer observer : observers) {
			observer.update(this.schoolMessage);
		}
		
	}
	
	public void setMessage(String s) {
		this.schoolMessage = s;
		System.out.println("学校的状态更新为:"+this.schoolMessage);
		notifyObserver();
	}
}

Student类,具体观察者实现类。

/**
 * 具体观察者类
 */
public class Student implements Observer {

	private String name;
	private String studentMessage;
	
	public Student(String name) {
		this.name = name;
	}
	
	@Override
	public void update(String message) {
		this.studentMessage =  message;
		System.out.println(this.name+"收到一条新的消息:"+this.studentMessage);
	}

}

测试类

public class ObserverTest {

	public static void main(String[] args) {
		School school = new School();
		Student xiaoming = new Student("小明");
		Student xiaoqing = new Student("小青");
		Student xiaoyun = new Student("小云");
		
		school.registerObserver(xiaoming);
		school.registerObserver(xiaoqing);
		school.registerObserver(xiaoyun);
		
		school.setMessage("周一升旗仪式要求穿校服。");
		
		System.out.println("--------------华丽丽的分割线----------------");
		//小云退学了
		school.removeObserver(xiaoyun);
		school.setMessage("周五晚上要召开家长会。");
	}

}

结果:

Java设计模式之观察者模式/发布订阅模式(Observer)_第3张图片

好了,就酱紫。用起来很方便,理解起来也比较简单。

3. 总结

3.1 特点

  • 松耦合,让耦合的双方都依赖于抽象,而不是依赖于具体。这也体现了依赖倒转原则。
  • 易维护、扩展和复用。

3.2 使用场景

当一个对象改变需要同时改变其他对象,并且它不知道有多少对象待改变的时候,可以考虑使用观察者模式。

3.3 不足

  • “抽象通知者”依赖“抽象观察者”,如果没有抽象观察者这样的接口,就不能完成“通知”的功能了。
  • 如果不是每个“具体观察者”都是完成“update”操作,就无法实现。比如说上面的例子,学校通知学生“周一升旗仪式要穿校服。”其实这个消息除了要通知学生,也应该通知老师,但老师还需要穿校服吗。即通知者的状态发生改变了,我们通知不同的观察者可能是不同的状态(或者产生不同的动作)。这个时候上面的模式就不适用了。

 

 

说在后面:
本文主要是小猫看《大话设计模式》的笔记式的记录,方便以后查阅。
当然了,我也阅读了许多其它博友相关的博文,再加之自己的理解整理出本文,仅供参考。

你可能感兴趣的:(设计模式,观察者模式,Observer,设计模式,Java,发布订阅模式)