观察者(Observer)模式的定义:指多个对象间存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新(做出不同的动作)。这种模式有时又称作发布-订阅模式、模型-视图模式,它是对象行为型模式。
模式的结构与实现
实现观察者模式时要注意具体目标对象和具体观察者对象之间不能直接调用,否则将使两者之间紧密耦合起来,这违反了面向对象的设计原则。
1. 模式的结构
观察者模式的主要角色如下。
- 抽象主题(Subject)角色:也叫抽象目标类,它提供了一个用于保存观察者对象的聚集类和增加、删除观察者对象的方法,以及通知所有观察者的抽象方法。
- 具体主题(Concrete Subject)角色:也叫具体目标类,它实现抽象目标中的通知方法,当具体主题的内部状态发生改变时,通知所有注册过的观察者对象。
- 抽象观察者(Observer)角色:它是一个抽象类或接口,它包含了一个更新自己的抽象方法,当接到具体主题的更改通知时被调用。
- 具体观察者(Concrete Observer)角色:实现抽象观察者中定义的抽象方法,以便在得到目标的更改通知时更新自身的状态。
观察者模式的结构图如图 1 所示。
说的直白点:
- 抽象主题:就是要做的整件事的框架,里面有观察者,观察者动作(这里的观察者的动作是个抽象方法,需要具体主题去调用真正观察者的动作方法)。(抽象主题是一个抽象类,当然他里面的观察者也是抽象的)
- 具体主题:就是实现抽象主题的类,主要作用是:实现观察者的动作(调用真正观察者的动作)
- 抽象观察者:是一个抽象类或者接口,就是为了在抽象主题中有一个统一的标准(举个例子:做菜需要盐(这里的盐就是接口),具体厨师用哪个品牌的盐都符合需要盐这一要求。)
- 具体观察者:是抽象观察者的具体实现。是真正做出反应的对象。
模式的应用实例
【例1】利用观察者模式设计一个程序,分析“人民币汇率”的升值或贬值对进口公司的进口产品成本或出口公司的出口产品收入以及公司的利润率的影响。
分析:当“人民币汇率”升值时,进口公司的进口产品成本降低且利润率提升,出口公司的出口产品收入降低且利润率降低;当“人民币汇率”贬值时,进口公司的进口产品成本提升且利润率降低,出口公司的出口产品收入提升且利润率提升。
这里的汇率(Rate)类是抽象目标类,它包含了保存观察者(Company)的 List 和增加/删除观察者的方法,以及有关汇率改变的抽象方法 change(int number);而人民币汇率(RMBrate)类是具体目标, 它实现了父类的 change(int number) 方法,即当人民币汇率发生改变时通过相关公司;公司(Company)类是抽象观察者,它定义了一个有关汇率反应的抽象方法 response(int number);进口公司(ImportCompany)类和出口公司(ExportCompany)类是具体观察者类,它们实现了父类的 response(int number) 方法,即当它们接收到汇率发生改变的通知时作为相应的反应。图 2 所示是其结构图。
代码实现
抽象观察者(可以是接口也可以是抽象类)
/**
* 将观察者抽象出来就是为了在抽象主题中可以可以直接使用Company就可以代表所有的观察者
* protected List<Company> companys ;
*/
//公司抽象类(
public abstract class Company {
//事情发生时,要做出的动作
abstract void doResponse(int number);
}
抽象主题(抽象类)
/**
*抽象主题类
* 目的:将主体聚合在一起,便于事件发生后统一动作处理
*/
public abstract class Rate {
//将能够影响到的观察者聚合在一起
protected List<Company> companys new ArrayList<>() ;
public void addCompony(Company company) {
companys.add(company);
}
public void delCompony(Company company) {
companys.remove(company);
}
//事件触发后,通知观察者 ;//实现该方法时就要调用真正的动作
public abstract void change(int number) ;
}
具体主题(要实现抽象主题的)
/**
* 具体主题:
* 目的:就是实现抽象主题的通知方法,真正调用观察者的动作方法
*/
public class RMBRate extends Rate {
@Override
public void change(int number) {
for (Company company : companys) {
company.doResponse(number);
}
}
}
具体观察者
/**
* 观察者一号:进口公司
*/
public class InputCompany extends Company {
@Override
// 事件发生后要做出的动作
void doResponse(int number) {
if(number>0)
System.out.println("汇率上涨,有利于进口公司");
else
System.out.println("汇率下跌,不利利于进口公司");
}
}
/**
*观察者二号:出口公司
*/
public class OutputCompany extends Company {
@Override
void doResponse(int number) {
if(number>0)
System.out.println("汇率上涨,不利于出口公司");
else
System.out.println("汇率下跌,有利于出口公司");
}
}
// 还可以有n多个其他的观察者
//.................
调用
public static void main(String[] args) {
Rate rate = new RMBRate();
rate.addCompony(new InputCompany());
rate.addCompony(new OutputCompany());
rate.change(10);
System.out.println("---------分割线-------");
rate.change(-10);
}
运行结果
总结
模式的应用场景
通过前面的分析与应用实例可知观察者模式适合以下几种情形。
- 对象间存在一对多关系,一个对象的状态发生改变会影响其他对象。
- 当一个抽象模型有两个方面,其中一个方面依赖于另一方面时,可将这二者封装在独立的对象中以使它们可以各自独立地改变和复用。
观察者模式是一种对象行为型模式,其主要优点如下。
- 降低了目标与观察者之间的耦合关系,两者之间是抽象耦合关系。
- 目标与观察者之间建立了一套触发机制。
它的主要缺点如下。
- 目标与观察者之间的依赖关系并没有完全解除,而且有可能出现循环引用。
- 当观察者对象很多时,通知的发布会花费很多时间,影响程序的效率。
模式的扩展
在 Java 中,通过 java.util.Observable 类和 java.util.Observer 接口定义了观察者模式,只要实现它们的子类就可以编写观察者模式实例。
- Observable类
Observable 类是抽象目标类,它有一个 Vector 向量,用于保存所有要通知的观察者对象,下面来介绍它最重要的 3 个方法。
void addObserver(Observer o) 方法:用于将新的观察者对象添加到向量中。
void notifyObservers(Object arg) 方法:调用向量中的所有观察者对象的 update。方法,通知它们数据发生改变。通常越晚加入向量的观察者越先得到通知。
void setChange() 方法:用来设置一个 boolean 类型的内部标志位,注明目标对象发生了变化。当它为真时,notifyObservers() 才会通知观察者。 - Observer 接口
Observer 接口是抽象观察者,它监视目标对象的变化,当目标对象发生变化时,观察者得到通知,并调用 void update(Observable o,Object arg) 方法,进行相应的工作。
/**
*
* Observable 抽象目标类
*/
public class OilPriceChange extends Observable {
private float price ;
public float getPrice() {
return price;
}
public void setPrice(float price) {
this.price = price;
}
// 就是调用该方法
public void post2Observer(float price) {
// 首先设置信息状态更改
super.setChanged();
//该方法会调用观察者的update方法
super.notifyObservers(price);
}
}
/**
* Observer 抽象观察者
*/
public class DF implements Observer {
@Override
public void update(Observable o, Object arg) {
float v = ((Float) arg).floatValue();
if(v>0) System.out.println("上涨 " +v+"空方很失落");
else System.out.println("下跌"+(-v)+"空方很高兴");
}
}
/**
* Observer 抽象观察者
*/
public class KF implements Observer {
@Override
public void update(Observable o, Object arg) {
float v = ((Float) arg).floatValue();
if(v>0) System.out.println("上涨 " +v+"多方很高兴");
else System.out.println("下跌"+(-v)+"多方很失落");
}
}
public class T {
public static void main(String[] args) {
OilPriceChange oilPriceChange = new OilPriceChange();
oilPriceChange.addObserver(new DF());
oilPriceChange.addObserver(new KF());
oilPriceChange.post2Observer(11.1f);
}
}
运行结果