什么是观察者模式
观察者模式,是一种基于事件和响应的设计模式,常用于传统的窗体应用程序(触发鼠标点击事件等)以及游戏开发领域(触发陷阱事件等)。
比如玩仙剑4的时候,点击“攻”按钮会发动普通攻击、点击“术”按钮会消耗神力发动法术、点击“技”按钮会消耗气力发动技能;在野外乱逛的时候,可能会遇到野怪进入战斗、也可能遇到宝箱获得宝物,还可能遇到存档点触发存档事件。
这些都是不同的触发事件,也要求程序对其有不同的响应,传统的想法有:
轮询:在程序中每隔一定事件查询事件是否发生,如果发生则进入对应的服务函数。但这种方法没有事件触发时程序在“空转”,会导致CPU使用率不高,且实时性不高;
类成员:将所有事件设为操作者这个类的成员,出现操作时调用对应的成员对象的方法,相当于触发了事件响应。但这种写法在以后出现新事件时(新野怪、新宝箱等),必须要去修改操作者类,不具备低耦合性。
这时我们就可以使用观察者模式。
观察者模式主要有两组对象:一个是观察者对象(野怪、宝箱、存档点等),一个是被观察者对象(主角等):所有观察者都需要实现 Observer 接口;所有被观察者,都继承自 Subject 抽象类。
Subject 类的成员 ObserverList ,存储已注册的观察者,当某个事件发生时,会通知列表中的所有观察者。
观察者模式的实现
我们这里使用 野怪、宝箱、存档点 的例子来展示观察者模式的Java实现:
首先是写出观察者接口以及被观察者抽象类:
public interface Observer{
//更新事件操作
public void update();
}
abstract public class Subject{
//观察者列表
private List<Observer> observerList = new ArrayList<>();
//添加观察者
public void attachObserver(Observer observer){
observerList.add(observer);
}
//去除观察者
public void detachObserver(Observer observer){
observerList.remove(observer);
}
//观察者更新
public void notifyObserver(){
for(Observer observer:observerList){
observer.update();
}
}
}
然后有多少种事件就定义多少个观察者类实现 Observer 接口:
//野怪
public class Monster implements Observer{
@Override
public void update(){
if(inRange()){
System.out.println("遭遇了怪物!");
}
}
private boolean inRange(){
//判断主角是否在野怪范围内
return true;
}
}
//宝箱
public class Chest implements Observer{
@Override
public void update(){
if(inRange()){
System.out.println("找到了宝箱!");
}
}
private boolean inRange(){
//判断主角是否在宝箱范围内
return true;
}
}
//存档点
public class Archive implements Observer{
@Override
public void update(){
if(inRange()){
System.out.println("到达了存档点!");
}
}
private boolean inRange(){
//判断主角是否在存档点范围内
return true;
}
}
而主角类只需要继承 Subject 类,并定义他自身的一系列操作即可:
//主角类
public class Hero extends Subject{
public void move(){
System.out.println("主角向前移动了一个距离");
notifyObserver();
}
//还可以添加后退、攻击、跳跃等操作,
//然后使用对应的方法对观察者列表进行触发即可
}
测试函数:
public static void main(String[] args) {
//由于定义的类都是Observer_patterns的普通内部类
//所以需要创建一个Observer_patterns实例对象
Observer_patterns ob = new Observer_patterns();
//创建对应对象
Hero hero = ob.new Hero();
Monster monster = ob.new Monster();
Chest chest = ob.new Chest();
Archive archive = ob.new Archive();
//添加会触发事件的对象
hero.attachObserver(monster);
hero.attachObserver(chest);
hero.attachObserver(archive);
//主角实例前进
hero.move();
}
当然,对于全地图的怪物、宝物、存档点等观察者对象来说,不需要实时监测主角是否进入范围,换言之,在主角移动时,只需要将陈旧的不在附近的观察者对象移出观察者列表,并将附近新出现的观察者对象放入观察者列表即可。