说明:本篇阐述的问题,是基于前面的游戏服务器架构设计的。算法
众所周知,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 }