前段时间一直在重构游戏里面的帮战系统,在重构的过程中几次都放到了外网去测试,结果出了很多问题,死锁一次,漏锁一次(导致coredump)。也正是在这次重构的过程中发现了如何设计健壮的类。
1: 整个的帮战系统主要是有两个类,一个玩家类LeagueBattler,一个战场类leaguebattlefield,分别记录各自的信息;之前的同事在注入进入战场逻辑、玩家被打死的逻辑等过程中,随意让玩家实例访问战场类,我们看下面的例子。
玩家进入帮战的处理逻辑,玩家在进入战场时,应该将对应帮派的参战人数加一。
int leaguebattler::enter_scene()
{
leaguefield *field = NULL;
this->monitor()->find(this->role_id(),field);
if(field == NULL)
{
return -1;
}
field->join_number[this->league_id()] ++;
return 0;
}
在玩家被打死的处理状态,玩家被打死时,应该将killer所在的帮派加分。
int leaguebattler::die_process()
{
leaguefield *field = NULL;
this->monitor()->find(this->role_id(),field);
if(field == NULL)
{
return -1;
}
field->mark[this->killer.league_id]++;
return 0;
}
类似上面例子很多,玩家的进入或者死亡需要更新战场信息的时候,我之前的程序员居然直接去修改帮派战场信息的数据,就是在类似上述的一个函数中造成了宕机。
2: 后来我们对整个设计进行了重构,重构之后第二个例子变成了这个样子。
int leaguebattler::die_process()
{
leaguefield *field = NULL;
this->monitor()->find(this->role_id(),field);
if(field == NULL)
{
return -1;
}
field->update_mark_by_league(this->killer->league_id());
return 0;
}
int leaguefield::update_mark_by_league(int league_id)
{
ACE_GUARD_RETURN(RW_MUTEX,mon,this->field_mutex,-1);
this->mark[league_id] ++;
return 0;
}
这样就避开了两个类之间的相互访问对方数据。类设计的一个原则就是对自己资源的绝对控制,不能被随意访问,一方面是容易维护、另一反面就是不会出现coredump的问题。
不会出现诸如由于漏锁引起的coredump等问题。