定义:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时所有依赖于他的对象自动收到通知并跟新。
举个例子:张三,李四,王五,收看CCTV(定义对象间的一种一对多的依赖关系) , CCTV放新闻张三,李四,王五收看新闻,CCTV放广告张三,李四,王五换台(一个对象的状态发生改变【CCTV】时所有依赖于他的对象自动收到通知并跟新)
观察者模式的实现有固定套路首先我们先熟记模板代码。
命名建议
目标接口定义建议在后面跟Subject
观察者接口的定义建议在后面跟Observer
观察者接口的跟新方法,建议名称为update,当然方法的参数可以根据需要定义,参数个数不限,参数类型不限。
模板代码
目标对象
/**
*目标对象被观察者的观察的对象(例子中的CCTV)并提供注册和删除观察者接口
* @author G410
*
*/
public class Subject {
//用来保存注册的观察者 张三李四王五
private List<Observer> observers = new ArrayList<>();
//注册观察者
public void attach(Observer observer)
{
observers.add(observer);
}
//删除观察者
public void detach(Observer observer)
{
observers.remove(observer);
}
//通知所有的观察者 一般目标对象状态变化后调用该方法通知观察者
public void notifyObserver()
{
for(Observer o:observers)
{
//this把目标对象的所有信息都传递给了观察者,观察者根据目标对象的状态改变做出相应的反应
o.update(this);
}
}
}
具体的目标对象
/**
*具体的目标对象,负责把有关状态存入相应的观察者对象(一个真正能放节目的CCTV,把节目发送给张三李四王五)
*
*
*
*/
public class ConcreteSubject extends Subject {
//目标对象的状态(CCTV现在可以放节目放广告放新闻)
private String subjectState;
public String getSubjectState() {
return subjectState;
}
//目标对象状态改变
public void setSubjectState(String subjectState) {
this.subjectState = subjectState;
//状态发生了改变通知注册的观察者(CCTV换节目了每个人有不同的反应)
this.notifyObserver();
}
}
观察者接口
//观察者接口定义一个更新接口给那些在目标状态改变时传入目标对象
public interface Observer {
public void update(Subject subject);
}
具体观察者对象
/**
* 具体观察者对象,实先跟新的方法,使自身状态与目标状态一致
*
*
*/
public class ConcreteObserver implements Observer {
//观察者的状态(记录状态留着备用)
private String observerState;
@Override
public void update(Subject subject) {
observerState=((ConcreteSubject)subject).getSubjectState();
}
}
光有模板还是很难理解观察者模式下面我们再通过开始时那个具体的例子套用模板加深理解
我们实现一个类表示CCTV 相当于模板中的ConcreteSubject
public class CCTV extends Subject {
//CCTV现在正在播放的节目
private String nowContent;
public String getNowContent() {
return nowContent;
}
public void setNowContent(String nowContent) {
this.nowContent = nowContent;
//节目改变通知看节目的人
this.notifyObserver();
}
}
实现一个Persion类相当于模板中的ConcreteObserver
public class Persion implements Observer{
//用户名
String name;
//记录用户现在再看的节目
private String content;
public Persion(String name)
{
this.name=name;
}
@Override
public void update(Subject subject) {
//取出CCTV正在播放的节目
content=((CCTV)subject).getNowContent();
//CCTV播放不同的节目观众有相应的反应
if(content.equals("广告"))
{
System.out.println(this.name+" 换台");
}
else
{
System.out.println(this.name+"觉得 "+this.content+"真好看");
}
}
}
这样观察者模式就实现好了下面编写测试类
public class Test {
public static void main(String[] args) {
//有一个CCTV电视台
CCTV cctv = new CCTV();
//有三个人看电视
Persion zhangsan=new Persion("张三");
Persion wangwu = new Persion("王五");
Persion lisi = new Persion("李四");
//注册观察者
cctv.attach(zhangsan);
cctv.attach(wangwu);
cctv.attach(lisi);
System.out.println("======现在播放的是新闻联播=========");
cctv.setNowContent("新闻联播");
System.out.println("======现在播放的是焦点访谈=========");
cctv.setNowContent("焦点访谈");
System.out.println("======现在播放的是广告=========");
cctv.setNowContent("广告");
}
}
运行输出:
Java中已经有现成的Subject类和Observer接口不需要我们在写一遍现在我们用Java中已有Observable类和Observer接口实现刚才的例子
使用Java中的已有功能实现观察者与前面自己实现观察者相比有如下改变
#不在需要定义观察者和目标接口,jdk帮忙定义了
#具体的目标实现里面不需要再维护观察者的注册信息了,这个在Java中Observable类里面已经帮忙实现好了
#触发通知有了一点变化,要先调用setChanged()告诉目标类要出发状态改变
修改后的CCTV
//Observable类已经实现了notifyObservers()方法
public class CCTV extends java.util.Observable {
//CCTV现在正在播放的节目
private String nowContent;
public String getNowContent() {
return nowContent;
}
public void setNowContent(String nowContent) {
this.nowContent = nowContent;
//在用Java中的Observer模式时这句话不可少
this.setChanged();
//节目改变通知看节目的人
this.notifyObservers(this.nowContent);
}
}
修改后的Persion
import java.util.Observable;
public class Persion implements java.util.Observer{
//用户名
String name;
//记录用户现在再看的节目
private String content;
public Persion(String name)
{
this.name=name;
}
@Override
public void update(Observable o, Object arg) {
//取出CCTV正在播放的节目
content=((CCTV)o).getNowContent();
//CCTV播放不同的节目观众有相应的反应
if(content.equals("广告"))
{
System.out.println(this.name+" 换台");
}
else
{
System.out.println(this.name+"觉得 "+this.content+"真好看");
}
}
}
测试类注册观察者方法也有所改变
public class Test {
public static void main(String[] args) {
//有一个CCTV电视台
CCTV cctv = new CCTV();
//有三个人看电视
Persion zhangsan=new Persion("张三");
Persion wangwu = new Persion("王五");
Persion lisi = new Persion("李四");
//注册观察者
cctv.addObserver(zhangsan);
cctv.addObserver(wangwu);
cctv.addObserver(lisi);
System.out.println("======现在播放的是新闻联播=========");
cctv.setNowContent("新闻联播");
System.out.println("======现在播放的是焦点访谈=========");
cctv.setNowContent("焦点访谈");
System.out.println("======现在播放的是广告=========");
cctv.setNowContent("广告");
}
}
运行输出:
参考资料:大话设计模式 研磨设计模式