前言
本篇谈谈观察者模式。
正文
概念
我们在开发的时候,常常会有这样的需求场景:一个对象的某个状态更新了,与这个对象相关的所有对象都要跟着更新。但这些相关对象又不能直接写在类中,这时候我们就需要一个观察者帮助我们在一个对象更新后,通知其他对象跟着更新。
常见的场景有:发布订阅模型,例如公众号的发布就属于被观察者,而用户属于观察者。再比如游戏开发中,当角色触发某个机制时,对应通知相关的增益或者负增益,这些都可以使用观察者模式来解决。
初步
最近在写的一个游戏脚本中,有这么一个场景:当角色进行攻击之后,附近如果有增益道具就会回复血量。
最简单的设计方法是设计一个事件循环,每隔一段时间判断角色是否攻击完成,并且是否判断道具是否在角色附近,但这个会形成空转,容易造成资源的浪费,并且无法进行实时的检测。
其次我还想到一种方法,那就是让把道具当作成员写进角色类,然后当角色攻击后,通知道具进行检测。代码如下:
1 2 3 4 5 6 7
| public class Role { Buff buff; public void attack(){ System.out.println("角色攻击"); buff.update(); } }
|
那这个代码的问题在于Buff类本身就不属于Role,并且如果后续需要添加更多增益效果,那就必须修改Role类,导致类与类过于耦合。
这时候我们就需要观察者模式,减少代码之间的耦合。
实现
在观察者模式中,有两个角色,观察者(Observer)与被观察者(Subject),其中被观察者中有一个List,存放着所有注册的观察者们,当被观察者更新时,通知其他观察者相应进行更新。就按照上面这个例子,我来写写代码:
我们先创建抽象类Subject,即被观察者以及创建Observer接口,即观察者:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| abstract public class Subject { private List<Observer> observerList = new ArrayList<>(); public void addObserver(Observer observer){ observerList.add(observer); } public void delObserver(Observer observer){ observerList.remove(observer); } public void notifyObserver(){ for (Observer observer : observerList){ observer.update(); } } }
public interface Observer { public void update(); }
|
接着我们创建增益类,实现观察者接口:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public class Buff implements Observer{
public boolean inNear(){ return true; }
@Override public void update() { if (inNear()){ System.out.println("角色获得Buff"); } } }
|
然后创建角色类,继承抽象类被观察者Subject,
1 2 3 4 5 6
| public class Role extends Subject{ public void attack(){ System.out.println("角色攻击"); notifyObserver(); } }
|
接着我们需要Client类实现一个客户端,注册所有观察者:
1 2 3 4 5 6 7 8 9 10
| public class Client { public static void main(String[] args) { Role role = new Role(); Buff buff = new Buff(); role.addObserver(buff); role.attack(); } }
|
我们运行查看输出:
这里有一点,在被观察者注册事件中,我们既可以像上文一样显式进行注册,又可以在被观察者的构造函数中就进行注册,我们只需要把观察者对象传入被观察者中,然后进行注册就可以了,算是两种不同的写法吧,上文的写法更加清楚,并且依赖关系并没有那么强。
其次我们还可以实现更多的观察者,在client中进行注册,使得多个事件可以同时进行更新。例如我们可以添加掉落物品,附近怪物会被吸引等等。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| public class monster implements Observer{ public boolean isNear(){ return true; } @Override public void update() { if (isNear()){ System.out.println("附近怪物被吸引"); } } } public class Treasure implements Observer{ public boolean isNear(){ return true; } @Override public void update() { if (isNear()){ System.out.println("获得宝箱"); } } }
public class Client { public static void main(String[] args) { Role role = new Role(); Buff buff = new Buff(); Treasure treasure = new Treasure(); Monster monster = new Monster(); role.addObserver(buff); role.addObserver(treasure); role.addObserver(monster); role.attack(); } }
|
优缺点
使用观察者模式的优点就是解耦,使对象之间的关系弱化,并且可以统一触发机制。
而缺点则是被观察者如果有许多观察者的话,通知所有的观察者可能需要花费很多时间。并且由于一个类既可以是观察者也可以是被观察者,如果有循环依赖的话,会导致不断调用。其次观察者模式只能知道目标发生了改变,但却无法知道怎么发生变化的。
后言
本文总结了观察者模式,写这个模式是因为最近在写的一个脚本中出现了类似的需求,就先把这个模式整理重新学习了一遍。