游戏里的设计模式之观察者模式
观察者模式
观察这样一来,当一个对象改变状态时,它的所有依赖者都会收到通 知自动更新。 ---------《head first 设计模式》
观察者模式的优缺点以及解决方案和思想
优点:
- 降低了目标与观察者之间的耦合关系,两者之间是抽象耦合关系。
- 目标与观察者之间建立了一套触发机制。
缺点:
- 目标与观察者之间的依赖关系并没有完全解除,而且有可能出现循环引用。
- 当观察者对象很多时,通知的发布会花费很多时间,影响程序的效率。
理解:
- 关于低耦合的情况,就是观察者都需要实现Observer这个接口,其实可以通过自定义注解加反射来解决这个问题,具体可以参考EventBus的@Subscribe注解
- 关于观察多对象过多的情况,一般可以通过异步的方法来解决,但是在操作的时候,如果只是单纯的通知问题不大,但是如果要修改数据之类的,就要注意线程的问题了
- 其实在实际场景中,观察者模式进场会变种使用,比如一些模式中都设计到观察者模式的思想,比如事件驱动模式,生产消费模式,通知订阅模型等等,在实际中使用,要学会变种,但是oop的原则不变,保持高内聚,低耦合的设计思想。
- 其实观察者模式很好的阐述了 oop原则的 为交互对象之间的松耦合设计而努力
游戏场景中应用
话不多说,观察者模式我就简单的介绍下,接下来主要想表达这个模式在游戏中的应用。其实在游戏中,观察者模式用到的地方真的很多,比如:交易行的关注模块(你关注了哪些商品,商品上架需要通知你),竞价模块(当你竞价的商品价格变高了,需要通知你), 游戏的任务模块(爬塔任务完成需要通知接了爬塔任务的模块,充值任务完成需要通知等等)… 下面我介绍下我们游戏中任务模块的观察者使用具体使用
我大概画了下类图
我一个个来解释
GameEvent 作为看成观察者模式中的 1主题,比如killMonsterEvent杀怪事件,假如任务A:完成杀一百只怪,成长目标:完成杀怪二百只,那这两个都是监听杀怪事件。并且killMonsterEvent可以传递需要的变量比如怪物数量。
你肯定会好奇:为什么接口下实现了3个event?
其实主要是数据一致性的问题。大概说下我们游戏的线程模型,我们游戏的分为 玩家线程,公共线程,场景线程等,不同的线程消费对应的消息,比如玩家升级自己的装备,完成杀怪任务等都是改变自己的数据该行为消息由玩家线程消费,而交易行购买商品,所有玩家共有的商品,所以购买,上架等操作都是走公共线程。还有一个Absevet是代表同线程执行,就是抛出事件的线程。 所以说在实际场景中使用,会经常操作数据库
下面直接上代码
public interface GameEventListener<T extends GameEvent> {
/**
* 事件处理的方法
*/
void handleEvent(T event) throws Exception;
}
@ListenerEvent(CommonAbandonGuildTaskEvent.class)
public class GiveUpGuildSTaskListener implements GameEventListener<CommonAbandonGuildTaskEvent> {
@Override
public void handleEvent(CommonAbandonGuildTaskEvent event) throws Exception {
}
}
public class EventCommandModular implements EventModular {
private static Logger logger = LoggerFactory.getLogger(EventCommandModular.class);
private static final EventCommandModular EVENT_COMMAND_MODULAR = new EventCommandModular();
private static Map<Class<? extends GameEvent>, Set<GameEventListener>> listenersMap = new LinkedHashMap<>();
public static EventCommandModular getInstance() {
return EVENT_COMMAND_MODULAR;
}
/**
* 扫描指定包下面的事件; 并将其注册 通过自定义注解@ListenerEvent
*/
public void scanRegisterListeners(String packageName) {
}
/**
* 发布事件
*
* @param event 事件
*/
public void publishEvent(GameEvent event) {
//这里可以优化
if (event instanceof RoleEvent) {
processRoleEvent(event);
return;
}
if (event instanceof CommonEvent) {
processCommonEvent(event);
return;
}
//同线程消费
Set<GameEventListener> listeners = listenersMap.get(event.getClass());
if (listeners == null) {
return;
}
for (GameEventListener listener : listeners) {
try {
listener.handleEvent(event);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}