先来领会一下观察者模式的精神:

观察者模式定义了一系列对象之间一对多的关系。通俗的比喻相当于报社和订报的人,水电局和用户。也就是出版者和订阅者。

观察者模式涉及到了如下几点定义:

抽象主题角色

具体主题角色

抽象观察者角色

具体观察者角色

1.接下来是具体的例子

就拿某直播平台的通知功能来说,其类图如下:(Tuhao就是土豪)

java 使用opencv maven_System

假设游戏是一个大的主题,在这里它相当于一个抽象主题角色:

public interface Game {
void addPeople(People people);
void removePeople(People people);
void notifyPeople();
}

抽象主题角色最基本具有添加,移除以及通知观察者的功能

然后是具体主题角色,例如在游戏分类下的StarCraft就是其中的一个游戏(具体主题角色):

public class StarCraft implements Game {
private ArrayList viewer;
private String message;
public StarCraft() {
viewer = new ArrayList();
}
public void addPeople(People people) {
viewer.add(people);
}
public void removePeople(People people) {
if (viewer.contains(people)) {
viewer.remove(people);
} else {
System.out.println("No viewers!");
}
}
public void setMessage(String message) {
this.message = message;
}
public void notifyPeople() {
for (People people : viewer) {
people.update(message);
}
}
}

它维护着该主题下的用户(观察者)列表,负责移除,添加观察者,发送通知等一系列操作。

接下来就是抽象观察者角色了:

public interface People {
void update(String message);
}

最后就是具体观察者角色,例如上斗鱼看直播的土豪:

public class Tuhao implements People {
public void update(String message) {
System.out.println("Watch: " + message+" and send money!");
}
}

最后就是测试了:

斗鱼某主播上开了直播,然后系统会通知订阅该直播的人直播开始了。

测试:

@Test
public void test() {
StarCraft starCraft = new StarCraft();
starCraft.setMessage("干死黄旭东!");
//土豪mike
Tuhao mike = new Tuhao();
starCraft.addPeople(mike);
starCraft.notifyPeople();
}
//Watch! and send money!

2.在Java JDK中,自带有java.util.Observable以及Observer分别为抽象主题角色以及抽象观察者角色。

使用它们改写观察者的关系如下:

java 使用opencv maven_Game_02

Observable默认使用Vector保存期观察者,且对其中的大部分方法都加了synchronized进行同步。不过,Observable是个类,继承了它就不能继承其他的类了。

此外,还增加了changed标志,只有当changed标志为true的时候,通知才可进行,且通知完成后,会将标志重置为false:

其代码如下:

public class Observable {
private boolean changed = false;
private Vector obs;
public Observable() {
obs = new Vector<>();
}
public synchronized void addObserver(Observer o) {
if (o == null)
throw new NullPointerException();
if (!obs.contains(o)) {
obs.addElement(o);
}
}
public synchronized void deleteObserver(Observer o) {
obs.removeElement(o);
}
public void notifyObservers() {
notifyObservers(null);
}
public void notifyObservers(Object arg) {
/*
* a temporary array buffer, used as a snapshot of the state of
* current Observers.
*/
Object[] arrLocal;
synchronized (this) {
if (!changed)
return;
arrLocal = obs.toArray();
clearChanged();
}
for (int i = arrLocal.length-1; i>=0; i--)
((Observer)arrLocal[i]).update(this, arg);
}
public synchronized void deleteObservers() {
obs.removeAllElements();
}
protected synchronized void setChanged() {
changed = true;
}
protected synchronized void clearChanged() {
changed = false;
}
public synchronized boolean hasChanged() {
return changed;
}
public synchronized int countObservers() {
return obs.size();
}
}

具体主题:

public class StarCraft extends Observable {
public void setChanged(){//Observable的setChanged为protected,必须在主题实现中实现
super.setChanged();
}
}

具体观察者:

public class Tuhao implements Observer{
public void update(Observable o, Object arg) {
System.out.println("土豪 online!");
if(arg!=null){
System.out.println(arg.toString());
}
}
}

测试:

@Test

public void test1(){
StarCraft starCraft = new StarCraft();
Tuhao mike = new Tuhao();
starCraft.addObserver(mike);
//可以查看状态是否改变
boolean status = starCraft.hasChanged();
System.out.println("状态是否改变:"+status);
starCraft.notifyObservers();
//可以查看共有多少待通知的对象
System.out.println("将通知人数为:"+starCraft.countObservers());
//改变状态,这里是重写了Observable的setChanged方法
starCraft.setChanged();
status = starCraft.hasChanged();
System.out.println("状态是否改变:"+status);
starCraft.notifyObservers();
System.out.println("通知人数为:"+starCraft.countObservers());
//通知完之后,状态会恢复原样,即为false,等待下一次需要状态的改变
status = starCraft.hasChanged();
System.out.println("状态是否改变:"+status);
}
// 状态是否改变:false
// 将通知人数为:1
// 状态是否改变:true
// 土豪 online!
// 通知人数为:1
// 状态是否改变:false
此外,还可以调用:
starCraft.notifyObservers("额外信息");
方法传递额外的信息。

3.最后,在Java GUI中,监听器的添加使用的就是观察者模式:

JButton j = new JButton();
j.addActionListener(new AbstractAction() {
public void actionPerformed(ActionEvent e) {
System.out.println("got it!");
}
});

监听器的实现类(上面的匿名内部类)就是观察者,JButton就是主题。

如上:)

参考资料:

Head First 设计模式