定义对象间的一种一对多的依赖关系
观察者模式的结构中包括四种角色:
主题(Subject):主题是一个接口,该接口规定了具体主题需要实现的方法观察者(Observer):观察者是一个接口,该接口规定了具体观察者用来更新数据的方法。
具体主题(ConcreteSubject):具体主题是实现主题接口类的一个实例,该实例包含有可以经常发生变化的数据。具体主题使用一个集合,比如ArrayList,存放观察者的引用,以便数据变化时通知具体的观察者。
具体观察者(Concrete Observer):具体观察者是实现观察者接口的一个实例。具体观察者包含有可以存放具体主题引用的主题接口变量,以便具体观察者让具体主题将自己添加到具体主题的集合中,使自己成为它的观察者,或让这个具体的主题将自己从具体的主题中的观察者列表中删除,使自己不再是它的观察者。
例题:有一个大学毕业生和一个海归留学者都希望能及时知道“求职中心”最新的职业需求信息。
主题(Subject)
package com.subject;
import com.observer.IObserver;
public interface ISubject {
public void addObserver(IObserver o);
public void deleteObserver(IObserver o);
public void notifyObservers();
}
具体主题(ConcreteSubject)
package com.subject;
import java.util.ArrayList;
import java.util.List;
import com.observer.IObserver;
/**
* 求职中心,即观察者模式中的:具体主题
* */
public class SeekJobCenterImpl implements ISubject {
//用于存储:求职信息
private String msg;
//用于表示求职信息内容是否发生变化,如果变化则通知所有求职者
private boolean changedFlag;
//用于存储所有求职者(观察者)
private List<IObserver> observersList;
public SeekJobCenterImpl(){
this.observersList = new ArrayList<IObserver>();
this.msg = "";
this.changedFlag = false;
}
//向求职者(观察者)列表中添加一个具体的求职者(观察者)
public void addObserver(IObserver o) {
if(!this.observersList.contains(o)){
this.observersList.add(o);
}
}
//从求职者(观察者)列表中删除一个具体的求职者(观察者)
public void deleteObserver(IObserver o) {
if(this.observersList.contains(o)){
this.observersList.remove(o);
}
}
//当数据发生变化时,为所有观察者更新数据信息
public void notifyObservers() {
if(this.changedFlag){
if(this.observersList != null && this.observersList.size() > 0){
for(int i=0; i<this.observersList.size(); i++){
IObserver observer = this.observersList.get(i);
//调用求职者(观察者)更新数据的方法为其更新数据
observer.hearTelephone(this.msg);
}
//所有求职者(观察者)数据更新完毕后,将标志位changedFlag设置为false,表示没有新数据需要更新
this.changedFlag = false;
}
}
}
//求职中心(具体主题)设置发布求职信息(数据)使用的方法
public void setJobInfo(String msg){
if(msg != null && !msg.trim().equals("")){
if(this.msg.equals(msg)){
this.changedFlag = false;
}else{
this.msg = msg;
this.changedFlag = true;
}
}
}
}
观察者(Observer)
package com.observer;
public interface IObserver {
public void hearTelephone(String msg);
}
具体观察者(Concrete Observer)此处以海归为例
package com.observer;
import java.io.FileWriter;
import java.io.IOException;
import com.subject.ISubject;
public class HaiGuiImpl implements IObserver {
//用于存放具体主题
private ISubject subject;
public HaiGuiImpl(ISubject subject){
this.subject = subject;
//在创建具体求职者(观察者)时,将自己(观察者)加入到具体主题中的观察者列表中
this.subject.addObserver(this);
}
//求职者(观察者)用于更新数据的方法
public void hearTelephone(String msg) {
//使用的IO流,用于向文本文件中写入更新的数据
FileWriter fw = null;
boolean flag = false;
try {
//用于表示求职信息中是否包含:"程序员" 或 "软件"等内容,如果包括,则该观察者将求职信息保存到文件中
if(msg.contains("程序员") || msg.contains("软件")){
//第2个参数为true,表示向文件中追加信息,否则将使用新内容覆盖原有内容
fw = new FileWriter("c:/HaiGui.txt", true);
//向文件中写入新数据
fw.write("我收到了一条新的求职信息:" + msg + "\n\r");
System.out.println("我是一个留学生,收到了一条新的求职信息:" + msg);
}else{
System.out.println("我是一名留学生,此次没有我需要的求职信息。。。");
}
} catch (IOException e) {
e.printStackTrace();
} finally{
try {
if(fw != null){
fw.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
测试:
package com.test;
import com.observer.HaiGuiImpl;
import com.observer.IObserver;
import com.observer.UniversityStudentImpl;
import com.subject.ISubject;
import com.subject.SeekJobCenterImpl;
public class Application {
//主程序,用于演示:观察者模式
public static void main(String[] args) {
//创建求职中心,即:具体的主题
SeekJobCenterImpl center = new SeekJobCenterImpl();
/**
* 创建求职者,即:具体的观察者
* universityStudent:代表大学生
* haiGui:代表留学生
*/
UniversityStudentImpl universityStudent = new UniversityStudentImpl(center);
HaiGuiImpl haiGui = new HaiGuiImpl(center);
//求职中心(具体主题)发布新求职信息(数据发生变化)
center.setJobInfo("腾辉公司需要招聘10名Java程序员。");
//具体主题通知观察者,更新数据
center.notifyObservers();
//求职中心(具体主题)发布新求职信息(数据发生变化)
center.setJobInfo("海景公司需要招聘9名动画设计师。");
//具体主题通知观察者,更新数据
center.notifyObservers();
//求职中心(具体主题)发布新求职信息(数据发生变化)
center.setJobInfo("仁海公司需要招聘9名电工。");
//具体主题通知观察者,更新数据
center.notifyObservers();
//求职中心(具体主题)发布了已有的求职信息(即:数据未发生变化)
center.setJobInfo("仁海公司需要招聘9名电工。");
//具体主题通知观察者,但观察者不会执行更新数据操作,因为数据没有变化。
center.notifyObservers();
}
}
优点:
具体主题和具体观察者是松耦合关系。由于主题(Subject)接口仅仅依赖于观察者(Observer)接口,因此具体主题只是知道它的观察者是实现观察者(Observer)接口的某个类的实例,但不需要知道具体是哪个类。同样,由于观察者仅仅依赖于主题(Subject)接口,因此具体观察者只是知道它依赖的主题是实现主题(subject)接口的某个类的实例,但不需要知道具体是哪个类。
观察模式满足“开-闭原则”。主题(Subject)接口仅仅依赖于观察者(Observer)接口,这样,我们就可以让创建具体主题的类也仅仅是依赖于观察者(Observer)接口,因此如果增加新的实现观察者(Observer)接口的类,不必修改创建具体主题的类的代码。同样,创建具体观察者的类仅仅依赖于主题(Observer)接口,如果增加新的实现主题(Subject)接口的类,也不必修改创建具体观察者类的代码。