应用场景
使对象在每次发生状态的改变时,都通知它的观察者。
观察者模式的构造过程
- 定义观察者接口,包含1个消息处理方法。
- 定义被观察者接口,包含注册/解除/通知观察者方法。
- 在被观察者中组合观察者对象,并在动作执行中调用观察者的消息处理方法。
代码示例
需求说明:本文拟定了“烽火台”的运转模式。烽火台即是观察者,也是被观察者。
/**
* 被观察者接口
*/
public interface Subject {
void registerObserver(Observer observer);
void removeObserver(Observer observer);
void notifyAllObserver();
void notifyObserver(Observer observer);
}
/**
* 观察者接口
*/
public interface Observer {
void doSomething(Subject subject);
}
/**
* 烽火台(即是观察者,也是被观察者)
*/
public class GreatWall implements Subject, Observer {
//私有属性
private String name;
private boolean fired;
private List<Observer> obList = new ArrayList<>();
//构造器
public GreatWall(String name){
super();
this.name = name;
}
//私有方法
public void findEnemy(){
System.out.println(this.name + ":" +"点燃狼烟!");
fire(null);
}
private void fire(Subject subject){
fired = true;
notifyAllObserver();
}
//实现observer方法
@Override
public void doSomething(Subject subject) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
GreatWall wall = (GreatWall)subject;
if (!fired) {
fired = true;
System.out.println(name + ":" + "发现狼烟,来自" + wall.getName() + "。点燃狼烟!");
notifyAllObserver();
} else {
System.out.println(name + ":" + "发现狼烟,来自" + wall.getName());
}
}
//实现subject方法
@Override
public void registerObserver(Observer observer) {
this.obList.add(observer);
}
@Override
public void removeObserver(Observer observer) {
this.obList.remove(observer);
}
@Override
public void notifyAllObserver() {
if (obList != null && obList.size() > 0) {
for (Observer ob: obList) {
ob.doSomething(this);
}
}
}
@Override
public void notifyObserver(Observer observer) {
observer.doSomething(this);
}
//getter setter
public String getName() {
return name;
}
public boolean isFired() {
return fired;
}
public List<Observer> getObList() {
return obList;
}
}
/**
* 测试类
* 需求:模拟烽火台功能
*
*/
public class Test {
public static void main(String[] args) {
GreatWall wall1 = new GreatWall("山海关");
GreatWall wall2 = new GreatWall("函谷关");
GreatWall wall3 = new GreatWall("嘉峪关");
wall1.registerObserver(wall2);
wall1.registerObserver(wall3);
wall2.registerObserver(wall1);
wall2.registerObserver(wall3);
wall3.registerObserver(wall1);
wall3.registerObserver(wall2);
wall1.findEnemy();
}
}
执行结果:
这里已经实现了对观察者的通知功能。
发现问题:
狼烟由山海关发出,嘉峪关第一次看到狼烟是在函谷关而不是山海关。与构想的预警过程有误差。
原因在于GreatWall的NotifyAllObserver中对观察者逐一遍历,山海关先通知了函谷关,在函谷关执行了其dosomething方法、并调用嘉峪关dosomething后,山海关才调用了嘉峪关的dosomething方法。
解决方法如下
通过多线程实现对各观察者的通知同步
- 设计观察者通知线程
public class NoticeTread implements Runnable {
private Observer ob;
private Subject sub;
public NoticeTread(Observer ob, Subject sub){
super();
this.ob = ob;
this.sub = sub;
}
@Override
public void run() {
ob.doSomething(sub);
}
}
- 修改被观察者的notifyAllObserver方法,在该方法中逐个启用线程进行通知。
@Override
public void notifyAllObserver() {
if (obList != null && obList.size() > 0) {
for (Observer ob: obList) {
Thread noticeTread = new Thread(new NoticeTread(ob,this));
noticeTread.start();
}
}
}
执行结果
最后,得到了预想的结果。实现了对烽火台观察和传递警报的简单模拟。