4. 单例模式
4.1 单例模式的定义
在一个项目中,全局范围内,某个类的实例有且仅有一个,通过这个唯一实例向其他模块提供数据的全局访问,这种模式就叫单例模式。
单例模式在项目中主要是用于数据类Data和模块Module用的最多,
Data用于存放该活动类型下所有的活动数据;
Module用于处理表现层和数据层之间的交互和回调;
4.2 单例模式的种类
4.2.1 懒汉模式:
public class AvXXData : GameDataBase{
private AvXXData _isnt;
public AvXXData Inst{
get{
if(null == _isnt)
_inst = new AvXXData();
return _inst;
}
}
}
4.2.2 饿汉模式:
public class AvXXMod : GameBaseModule{
private AvXXMod = _inst = new AvXXMod();
public AvXXMod = Inst{
get { return _inst; }
}
}
4.3 单例模式的作用:
保证了游戏中各种数据类型和控制等各类模块在内存中只有一个,不会出现重复生成导致内存占用过多,或者一个对象频繁创建和销毁实例。
4.3.1 例子
在实际使用中,以游戏新出角色时,会出现角色卡池的情况下举例,如何使用单例模式保证角色卡池的数据唯一。
流程:玩家点击抽卡活动按钮 —> 向服务器请求卡池活动数据 —> 服务器返回卡池活动数据 —> 存储卡池活动数据并处理显示层
这里以鸣潮为例,终于有时间截个图了:
[图片]
图中所示的抽卡界面中,所有的活动卡池应该是会共用一个活动Id(可以使用卡池编号或者活动id来区分不同卡池信息,例如1001001和1001002分别代表1001抽卡活动,后面的001和002分别代表两个不同的卡池),对于同一个活动下多开的情况会使用一个单例模式的Data数据类存放该活动的所有卡池的具体数据,在这之下每一个卡池的info数据类就以字典的形式。代码实现如下:
public class AvGachaData : GameDataBase{
private AvGachaData _isnt;
public AvGachaData Inst{
get{
if(null == _isnt)
_inst = new AvGachaData();
return _inst;
}
}
// 用字典存储同一个活动下不同子活动的数据
// 例如,抽取角色是一个活动,但是我们可能同时开启多个活动
// 以鸣潮为例,现在有一个新角色弗洛洛,一个返厂角色洛可可,常驻池,并且有角色池和武器池之分
// 那么我们可以通过池子的id来从这里获取不同的池子的数据
private Dictionary<int, AvGachaInfo> _infoDic = new Dictionary<int, AvGachaInfo>();
#region 协议
MsgListener(Msg.SCAvGacha, OnSCAvGacha);
#endregion
private void OnSCAvGacha(message obj)
{
var msg = obj as SCAvGacha;
// 把协议内的数据转换成AvGachaInfo
_infoDic[msg.activityId] = info;
}
public AvGachaInfo GetInfo(int id)
{
// 依据活动id等信息获取对应的数据
}
}
// 单个卡池的基本信息
public class AvGachaInfo{
private int activityId; // 活动Id
private int roleId; // 角色Id
public int RoleId => roleId;
private EnumGachaType _type;
// 卡池抽数,保底次数,抽卡所需道具id等等信息都存放在这里
public AvGachaInfo(int AvId, ...){
// ...
}
}
// 卡池类型枚举
public enum EnumGachaType{
RoleNormal, // 角色普池
RoleUp, // 角色Up池
WeaponNomal, // 武器普池
WeaponUp, // 武器Up池
Other // 其他自定义
}
在这里我们就实现了一个游戏中活动的数据类基本框架,既保证了玩家再不需要对应数据时,数据为空,有保证了玩家在反复请求同一个数据时,数据不会重复的问题。同时我们还保证了一个活动多开的情况下可以正常分离同一个活动的不同实例。