基于Spring框架怎么构建游戏玩法服务

说明:本篇阐述的问题,是基于前面的游戏服务器架构设计的。算法

问题

众所周知,Spring最擅长的领域是无状态服务的构建,而游戏(尤为是玩法部分)是有状态的。以棋牌游戏为例,玩法服务里面大概涉及如下两类对象:
一、无状态的服务,好比数据读写、通讯等;
二、与游戏桌子绑定的有状态类,好比桌子自己,状态机,玩家的游戏状态等。
后者确定是要访问前者提供的方法的,那么后者怎么拿到前者的引用呢。服务器

咱们一开始的作法是,无状态的服务作成Spring Bean,而后在启动的时候,把这些service的引用放到一个静态了类的字段里面,相似这样:架构

public class ServiceContainer {
    public static ServiceBean1 bean1;
    public static ServiceBean2 bean2;
    ...
   
   public static init(ApplicationContext context)
    {
        bean1 = context.getBean(ServiceBean1.class);
        bean2 = context.getBean(ServiceBean2.class);
        ...
    }
}

而后有状态的对象就能够简单地经过ServiceContainer.bean1来访问无状态的服务了。
不过代码多了之后,发现这种方式实在有点糟糕。 因为对ServiceContainer的访问实在太简单了,致使你们随意地在里面添加新的字段,最后对象之间的耦合依赖很是混乱,彻底没有享受到使用Spring的好处。优化

解决办法

为了解决问题,首先确立一个原则,不容许经过静态方法、静态字段来暴露服务;单例模式固然也不容许(本质仍是静态方法),既然用了Spring,就要按Spring的架构哲学来搞。
第一个办法是这样的,在建立一个有状态的对象的时候,若是这个对象须要依赖某些Service,那么在初始化的过程当中手动注入进去,以游戏桌table为例,代码相似这样:架构设计

public class GameTable {
    public ServiceBean1 bean1;
    public ServiceBean2 bean2;
    ...
   
   public init(ApplicationContext context)
    {
        bean1 = context.getBean(ServiceBean1.class);
        bean2 = context.getBean(ServiceBean2.class);
        ...
    }
}

这个方法,虽然看起来和ServiceContainer半斤八两,但实际上要好不少,至少某个玩法的table依赖哪些服务一目了然。不过产品功能复杂性增长之后,table依赖的服务愈来愈多,仍是会让人很不爽。设计

继续优化方案

table里面注入了太多的service,本质上说明游戏玩法逻辑的拆分不够细。
在前边的文章里面,咱们的核心玩法逻辑拆分红table(桌子),state(状态机),messageProcessor(消息处理器),如今从新捋一下这些角色的职责:日志

一、table以及相关的类是游戏玩法的领域模型,专一于游戏的数据状态和基本规则算法;
二、state状态机用来简化流程控制,专一于消息的过滤,和状态的切换;
三、messageProcessor,专一于消息的处理,服务于玩法的全部桌子,也就是说它是无状态的;
四、其余业务逻辑,好比数据的存取,游戏日志处理,外围功能(好比任务)。code

其中table和state都是内存对象,而messageProcessor和其余逻辑都是标准的Service Bean,state将消息过滤后,委托给messageProcessor来处理,依据处理结果来迁移状态。
一个典型的messageProcessor接口可能相似这样的:对象

public boolean processPlayerJoin(Table table, Player player, Message message)
{
    if (玩家是否知足入桌条件)
    {
         table.addPlayer(player);
         return true;
    }
    return false
}
相关文章
相关标签/搜索