观察者模式是使用频率最高的设计模式之一,它用于建立一种对象与对象之间的依赖关系,一个对象发生改变时将自动通知其他对象,其他对象将相应作出反应。在观察者模式中,发生改变的对象称为观察目标(主题),而被通知的对象称为观察者,一个观察目标可以对应多个观察者,而且这些观察者之间可以没有任何相互联系,可以根据需要增加和删除观察者,使得系统更易于扩展。
再来看看观察者模式的定义:
观察者模式(Observer Pattern):在对象之间定义一对多的依赖,这样一来,当一个对象改变状态,依赖它的对象都会收到通知,并自动更新。
背景:
英雄联盟相信大家都不陌生,WeGame助手有个常用的功能就是野怪刷新提示,只要开启这个功能,玩家在游戏里面就能收到关于野怪的刷新时间。今天就来实现以下这个功能!
分析:WeGame为玩家提供野怪刷新提示功能 (WeGame是主题,可观察者)
玩家可以在WeGame中开启此功能(注册为观察者)
定义一个Subject (主题、可观察者)接口
/**
* @author zonkidd
* @create 2019-01-31
*/
public interface Subject {
public void registerObserver(Observer o); //注册Observer对象为观察者
public void removeObserver(Observer o); //移除观察者
public void notifyObserver(); //通知所有观察者
}
定义一个Observer (观察者)接口
/**
* @author zonkidd
* @create 2019-01-31
*/
public interface Observer {
public void update(String buff,String time);
}
定义一个具体WeGame类实现Subject接口
/**
* @author zonkidd
* @create 2019-01-31
*/
import java.util.ArrayList;
public class WeGame implements Subject{
private ArrayList observers; //记录观察者
private String buff=null;
private String time=null;
public WeGame(){
observers=new ArrayList ( );
}
public void registerObserver(Observer o){ //想要注册为观察者,调用此方法即可
observers.add ( o );
}
public void removeObserver(Observer o){//同样地,当观察者想取消注册,可调用这个方法
int i=observers.indexOf ( o );
if(i >=0)
observers.remove ( i );
}
public void notifyObserver(){ /*在这里,我们将把状态告诉每一个观察者,因为每个观察者
都实现了update(),所以可以通过这个方法通知它们。*/
for (int i = 0; i <observers.size () ; i++) {
Observer observer=(Observer) observers.get ( i ); //获得Observer对象
observer.update (buff,time);
}
}
public void neutralsChanged(){ //当获得野怪更新的状态,我们通知观察者
notifyObserver ();
}
public void setNeutrals(String buff,String time)
{
this.buff=buff;
this.time=time;
neutralsChanged ();
}
}
我们已经把WeGame类写出来了,现在该轮到Player类(观察者)。假设有三个玩家开启了该功能
/**
* @author zonkidd
* @create 2019-01-31
*/
public class Player1 implements Observer{
private String buff=null;
private String time=null;
private Subject weGame;
public Player1(Subject weGame){
this.weGame=weGame;
weGame.registerObserver ( this ); //注册该对象为观察者
}
public void update(String buff,String time){
this.buff=buff;
this.time=time;
display();
}
public void display(){
System.out.println("player1:收到:"+buff+"将于"+time+"s之后刷新");
}
}
Player2和Player3 省略,因为与Player1基本一样。
建立一个测试程序 WeGameMain
/**
* @author zonkidd
* @create 2019-01-31
*/
public class WeGameMain {
public static void main(String[] args){
WeGame weGame=new WeGame ();
Player1 player1=new Player1 ( weGame );
Player2 player2=new Player2 ( weGame );
Player3 player3=new Player3 ( weGame );
//接下来可以调用一个野怪状态更新的方法,看下Player1,Player2,Player3会不会收到
weGame.setNeutrals ( "红BUFF","20" );
weGame.setNeutrals ( "蓝BUFF","25" );
}
}
类图:
Java内置的观察者模式如何运作
和我们上面所实现的有所类似,但有一些小差异,最明显的是WeGame现在扩展自Observable类,并继承到一些增加、删除、通知观察者的方法(以及其他方法)、
实现观察者接口(java.util.Observer),然后调用任何Observable对象的addObserver()方法。不想再当观察者时,调用deleteObserver()方法即可。
先继承java.util.Observable类,然后
setChanged()存在的意义:
//Observable类的源码
protected synchronized void setChanged() {
changed = true;
}
public void notifyObservers(Object arg) {
/*
* a temporary array buffer, used as a snapshot of the state of
* current Observers.
*/
Object[] arrLocal;
synchronized (this) {
/* We don't want the Observer doing callbacks into
* arbitrary code while holding its own Monitor.
* The code where we extract each Observable from
* the Vector and store the state of the Observer
* needs synchronization, but notifying observers
* does not (should not). The worst result of any
* potential race-condition here is that:
* 1) a newly-added Observer will miss a
* notification in progress
* 2) a recently unregistered Observer will be
* wrongly notified when it doesn't care
*/
if (!changed)
return;
arrLocal = obs.toArray();
clearChanged();
}
for (int i = arrLocal.length-1; i>=0; i--)
((Observer)arrLocal[i]).update(this, arg);
}
当changed值为false时,跳出方法,不在执行下去
这样做有其必要性,setChanged()方法可以让你在更新观察者时,有更多的弹性,你可以更适当地通知观察者,比方说,如果没有setChanged()方法,WeGame的野怪时间计算可能每减少一秒就更新一次,这会造成WeGame(主题对象)持续不断地通知观察者,这样并不是我们想要的。如果我们希望在野怪还有20秒刷新时才更新,就可以在距离野怪刷新20秒才调用setChanged()方法,进行有效的更新。
update( Observable o, Object arg )
如果你想“推”(push)数据给观察者,就可以调用notifyObserver( Object arg ) ,
否则 观察者就必须从可观察者对象中“拉”(pull)数据。
WeGame改成继承Observable
import java.util.Observable;
/**
* @author zonkidd
* @create 2019-01-31
*/
public class WeGame extends Observable {
private String buff=null;
private String time=null;
public WeGame(){}
public void neutralsChanged(){
setChanged ();
notifyObservers (); /*注意:我们没有调用notifyObservers (arg) 传送数据对象
,这表示我们采用的做法是 "拉"*/
}
public void setMeasurements(String buff,String time){
this.buff=buff;
this.time=time;
neutralsChanged ();
}
public String getBuff() {
return buff;
}
public String getTime() {
return time;
}
}
定义 Player(观察者) 实现 Observer接口
import java.util.Observable;
import java.util.Observer;
/**
* @author zonkidd
* @create 2019-01-31
*/
public class Player1 implements Observer {
Observable observer;
private String buff=null;
private String time=null;
public Player1(Observable observable)
{
this.observer=observable;
observable.addObserver ( this ); //注册为观察者
}
public void update(Observable observable,Object arg){
if(observable instanceof WeGame){
WeGame weGame= (WeGame) observable //向下转型;
buff=weGame.getBuff();
time=weGame.getTime ();
display();
}
}
private void display() {
System.out.println("player1:收到:"+buff+"将于"+time+"s之后刷新");
}
}
Player2和Player3 省略
执行类WeGameMain
/**
* @author zonkidd
* @create 2019-01-31
*/
public class WeGameMain {
public static void main(String[] args) {
WeGame weGame=new WeGame ();
Player1 player1=new Player1 ( weGame ); //player1开启此功能
Player2 player2=new Player2 ( weGame ); //player2开启此功能
Player3 player3=new Player3 ( weGame ); //player3开启此功能
//经过一系列 运算,省略,weGame得到了野怪准备要刷新的内幕消息
// 调用野怪状态更新的方法
weGame.setNeutrals("红buff","20");
weGame.setNeutrals ("蓝buff","40");
}
}