目录
观察者模式(Observer)
理解
UML图
优缺点
观察者模式在各语言中的支持
Java
C#
实例
小丑表演
办公室摸鱼
投资者与股票
观察者模式(Observer)
本质:触发联动
目标对象变化时,会通知所有登记的观察者对象行动。
别名:
发布-订阅(Publish/Subscribe)模式
模型-视图(Model/View)模式
源-监听器(Source/Listener)模式
从属者(Dependents)模式。
理解
- 是一对多的单向依赖:多个观察者对象同时观察一个目标对象, 观察者依赖于目标。
- 相互观察时注意死循环
- 若 A、B观察C,B、C观察A,则需要两套观察模式,否则死循环
- 通知的顺序:同时
- 多个观察者是平行的(虽然是循环,但几乎同时完成)
减少对象之间的耦合有利于系统的复用,但同时需要使这些低耦合度的对象行动协调一致(观察者对象行动的方法要保持参数一致)
UML图:
Subject:抽象被观察者(目标)
- 一个目标可以被多个观察者观察。目标变化时,目标会通知所有登记的观察者
- 拥有一个抽象观察者的引用
Observer:抽象观察者
- 得到目标的通知时更新自己。更新方法,图上Updata()
ConcreteSubject:具体被观察者
- 将状态存入具体现察者对象。自己变化时,给所有登记的观察者发出通知
ConcreteObserver:具体观察者
- 拥有一个指向具体目标的引用
优缺点
优点:
- 实现了观察者和被观察者之间的抽象耦合。
- 动态联动
- 广播通信。被观察者会向所有的登记的观察者发出通知。
缺点:
可能会引起无谓的操作。
由于采用广播方式,不管观察者需不需要,每个观察者都会被调用update方法
观察者模式在各语言中的支持
Java
1.Observer接口
Observer为java.util包下的一个接口,源码如下:
public interface Observer {
void update(Observable o, Object arg);
}
该接口约定了观察者的行为
所有观察者需要在被观察对象发生变化时做出反应,所做的具体反应就是实现Observer接口的update方法。
- 当前接口代表观察者,要与被观察对象交互,需要持有被观察对象Observable的引用,因此update方法一个参数的类型是Observable
- 与调用者通信,则是使用Object类型的参数。该参数是调用者调用Observable实例的notifyObservers(Object obj)方法时传入的,也可以不传。
第一个参数是为观察者提供了一种拉取数据的方式,update中的业务可以根据所需去拉自己想要的被观察对象的信息(一般被观察对象中提供getter),第二个参数则是由调用者调用notifyObservers(Object obj)将一些信息推过来。通过这两个参数,观察者,被观察对象,调用者(调用通知刷新方法的可能是被观察对象本身,此时只存在观察者与被观察者两者)三者就联系起来了。
2.Observable类
java.util.Observable
该类实现了被观察者的功能
该类的成员变量和方法如下:
public class Observable {
private boolean changed = false;
private Vector<Observer> obs;
public Observable(){};
protected synchronized void setChanged(){};
protected synchronized void clearChanged(){};
public synchronized void addObserver(Observer o){};
public synchronized void deleteObserver(Observer o) {};
public synchronized void deleteObservers(){};
public synchronized boolean hasChanged(){};
public synchronized int countObservers(){};
public void notifyObservers(){};
public void notifyObservers(Object arg){};
}
C#
.NET中提供了Delegate与Event机制
实例
小丑表演
镇上来了一位小丑,为大家表演节目,所有观看的观众会根据小丑表演的精彩与否来做出相应的反应,比如表演的好就鼓掌喝彩,表演的不好就倒喝彩,表演完毕观众就退场。
分析:
- 观众是观察者,应该实现Observer接口
- 小丑是被观察者,应该继承Observable类
观众会根据小丑表演的状态改变自己的行为
package com.test;
import java.util.Observable;
import java.util.Observer;
//观众类
public class Viewer implements Observer{
private int id;
public Viewer(int id) {
super();
this.id = id;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
@Override
public void update(Observable o, Object arg) {
Integer state=(Integer) arg;
switch(state) {
case Clown.good:
applause();
break;
case Clown.bad:
cheerback();
break;
case Clown.complete:
exit();
break;
}
}
private void applause() {
System.out.println(id+"号观众鼓掌了");
}
private void cheerback() {
System.out.println(id+"号观众喝倒彩");
}
private void exit() {
System.out.println(id+"号观众退场了");
}
}
package com.test;
import java.util.Observable;
import java.util.Random;
//小丑类
public class Clown extends Observable{
public static final int good=0;
public static final int bad=1;
public static final int complete=2;
public void perform() {
System.out.println("小丑开始表演");
//0-2随机值
int random=new Random().nextInt(2);
switch(random) {
case good:
System.out.println("小丑表演得好");
break;
case bad:
System.out.println("小丑表演得不好");
break;
}
setChanged();
//小丑的状态传给观众的update方法的第二个参数
notifyObservers(random);
}
public void exit() {
System.out.println("小丑表演结束");
setChanged();
//小丑的状态传给观众的update方法的第二个参数
notifyObservers(complete);
}
}
package com.test;
public class Test {
public static void main(String[] args) {
Clown c=new Clown();
for(int i=1;i<4;i++) {
Viewer v=new Viewer(i);
c.addObserver(v);
System.out.println(v.getId()+"号观众入席");
}
c.perform();
c.exit();
}
}
办公室摸鱼
办公时间摸鱼, 在老板到来时,前台负责通知同事进入工作状态
- 观察者:两个同事,需要实现Observer接口
- 被观察者有两个:前台和老板,应该继承Observeable类
- 前台通知:摸鱼的人改为工作状态
- 老板通知:摸鱼的人被老板发现并且批评
package qiqi.work;
import java.util.Observable;
public class Boss extends Observable {
private int state=0;//离开
public void com() {
System.out.println("我大老板回来了");
state=2;//回来
setChanged();//必须要写
notifyObservers(state);//发出通知
}
}
package qiqi.work;
import java.util.Observable;
public class Secretary extends Observable{
private int state=0;//老板离开
public void see() {
System.out.println("老板回来了");
state=1;//老板回来啦
setChanged();
notifyObservers(state);
}
}
package qiqi.work;
import java.util.Observable;
import java.util.Observer;
public abstract class Observers implements Observer{
protected String name;
public Observers(String name) {
super();
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public abstract void update(Observable o, Object arg);
public abstract void see();
public abstract void exit();
public abstract void criticize();
}
package qiqi.work;
import java.util.Observable;
import java.util.Observer;
public class NBAObserver extends Observers{
public NBAObserver(String name) {
super(name);
// TODO Auto-generated constructor stub
}
@Override
public void update(Observable o, Object arg) {
// TODO Auto-generated method stub
Integer state=(Integer)arg;
switch(state){
case 0:
see();break;
case 1:
exit();break;
case 2:
criticize();break;//老板通知
}
}
@Override
public void see() {
// TODO Auto-generated method stub
System.out.println(name+"看NBA");
}
@Override
public void exit() {
// TODO Auto-generated method stub
System.out.println(name+"停止看NBA,开始工作");
}
@Override
public void criticize() {
// TODO Auto-generated method stub
System.out.println(name+"你居然在工作时间看NBA,快工作!");
}
}
package qiqi.work;
import java.util.Observable;
public class StockObserver extends Observers{
public StockObserver(String name) {
super(name);
// TODO Auto-generated constructor stub
}
@Override
public void see() {
// TODO Auto-generated method stub
System.out.println(name+"看股票");
}
@Override
public void exit() {
// TODO Auto-generated method stub
System.out.println(name+"停止看股票,开始工作");
}
@Override
public void criticize() {
// TODO Auto-generated method stub
System.out.println(name+"你居然在工作时间看股票,快工作!");
}
@Override
public void update(Observable o, Object arg) {
// TODO Auto-generated method stub
Integer state=(Integer)arg;
switch(state){
case 0:
see();break;
case 1:
exit();break;//秘书通知
case 2:
criticize();break;//老板通知
}
}
}
package qiqi.work;
public class Test {
public static void main(String[] args) {
NBAObserver nba=new NBAObserver("张三");
StockObserver stock=new StockObserver("李四");
Secretary s=new Secretary();
s.addObserver(nba);
s.addObserver(stock);
s.see();
Boss b=new Boss();
b.addObserver(nba);
b.addObserver(stock);
b.com();
}
}
投资者与股票
注册的投资者在股票市场发生变化时,可以自动得到通知的功能
当观察者数量无法确定时,可以用一个List来保存观察者
不使用Observer类和接口,代码如下:
package com.invest;
//观察者接口
public interface IInvestor {
void update(Stock stock);
}
package com.invest;
//具体观察者
public class Investor implements IInvestor{
private String name;
private Stock stock;
public Investor(String name) {
super();
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Stock getStock() {
return stock;
}
public void setStock(Stock stock) {
this.stock = stock;
}
@Override
public void update(Stock stock) {
System.out.println(name+" "+stock.symbol+" "+stock.price);
}
}
package com.invest;
import java.util.ArrayList;
//抽象被观察者
public abstract class Stock {
protected String symbol;
protected double price;
private ArrayList<Investor> investors=new ArrayList<Investor>();
public Stock(String symbol, double price) {
super();
this.symbol = symbol;
this.price = price;
}
public String getSymbol() {
return symbol;
}
public void setSymbol(String symbol) {
this.symbol = symbol;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
Notify();//状态改变,触发通知
}
public void Attach(Investor investor) {
investors.add(investor);
}
public void Detach(Investor investor) {
investors.remove(investor);
}
public void Notify() {
for(Investor i:investors)
i.update(this);
}
}
package com.invest;
//具体被观察者
public class QQ extends Stock{
public QQ(String symbol, double price) {
super(symbol, price);
// TODO Auto-generated constructor stub
}
}
package com.invest;
public class Test {
public static void main(String[] args) {
// TODO Auto-generated method stub
Investor i1=new Investor("张三");
Investor i2=new Investor("李四");
Stock s=new QQ("腾讯",120.1);
s.Attach(i1);
s.Attach(i2);
s.setPrice(120.4);
s.setPrice(121.1);
s.Detach(i2);
s.setPrice(122.2);
}
}
使用Observer接口和Observerable类,代码如下:
package com.invest1;
import java.util.Observable;
import java.util.Observer;
//观察者
public class Investor implements Observer{
private String name;
public Investor(String name) {
super();
this.name = name;
}
@Override
public void update(Observable o, Object arg) {
Integer a=(Integer)arg;
if(a==Stock.getState())
System.out.println(name+"买的股票:"+Stock.getSymble()+"价格变为"+Stock.getPrice());
}
}
package com.invest1;
import java.util.Observable;
public class Stock extends Observable{
private static String symble;
private static double price;
private static int state=0;//1表示状态变化,0表示不变
public Stock(String symble, double price) {
super();
this.symble = symble;
this.price = price;
}
public static String getSymble() {
return symble;
}
public void setSymble(String symble) {
this.symble = symble;
}
public static double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
change();//价格改变,被观察者的状态改变
}
public void change() {
setChanged();
setState(1);
notifyObservers(getState());//1表示状态变化,0表示不变
}
public static int getState() {
return state;
}
public static void setState(int state) {
Stock.state = state;
}
}
package com.invest1;
public class Main {
public static void main(String[] args) {
Investor i1=new Investor("张三");
Investor i2=new Investor("李四");
Stock s=new Stock("谷歌",120.1);
s.addObserver(i1);
s.addObserver(i2);
s.setPrice(122.1);
}
}