前言:
大家在写业务的时候有没有出现过,想找一个工具,但又不知道有没有实现,想封装一个工具,又怕别人不知道,工具的实现老是容易存在重复造轮子的情况。
根据笔者多年的工作经验总结大概是因为以下几种原因造成的:
1:工具划分不够清晰(例如所有工具都往一个叫utils或者gameutils的类里面去塞,根本不按职能进行细分,更有甚者我还见过在一个gameutil的文件里面封装了4个几乎一样的方法)
2:工具封装的不好,并不能做到很通用(比如参数多余,传参不规范造成了这个工具可能只能给我这个业务用,但是和我类似的业务缺用不了)
3:命名不规范,注释不好,然后代码实现也很糟糕,根本没办法让人一眼就明白他是干嘛的
4:工具不好找,根本都不知道实现了哪些工具(工具到处放,代码很乱,很难找到自己想要的工具)
等等
而这一期笔者主要介绍一下如何去统一封装,管理工具类,并对比一下之前的一些封装
笔者经历过各式各样的项目,每个项目对工具的封装方式都不一样,现在我们挑选几种比较常见的封装方案,并对其做一一解析。
一:使用饿汉式或者懒汉式将工具自己封装成单例
顾名思义,就是在单例类上实现自己的获取
//poolMgr.ts
//单例式工具类的实现
export class PoolMgr {
public static inst:PoolMgr = new PoolMgr();
public checkOut()
{
...
}
public checkIn()
{
...
}
}
//main.ts
//单例式工具类的调用
function test()
{
let a = PoolMgr.inst.checkOut();
}
优点:
实现简单,不需要去关心其它东西,整个实现+调用都在自己这里实现
缺点:
es6中容易出现循环引用的问题
每个工具类的实现都放在不同地方,不便于去查找,不便于统一管理,容易重复造轮子
二:封装到一个全局ts文件中
//poolMgr.ts
export class PoolMgr {
public checkOut()
{
...
}
public checkIn()
{
...
}
}
//main.ts
//全局唯一实体在游戏创建的时候就被实例化
export let gPoolMgr:PoolMgr = new PoolMgr();
优点:
解决了es6循环引用的问题
缺点:
1.还是不好找,虽然是放在一个文件里面实现,但是没办法在业务里面直接提示出来
2.依旧缺乏一个统一的管理
三:将所有的工具封装到一个工具类中
//poolMgr.ts
export class PoolMgr {
//工具类初始化
public init()
{
}
public checkOut()
{
...
}
public checkIn()
{
...
}
//工具类重置
public clear()
{
}
}
//Core.ts
//全局唯一实体在游戏创建的时候就被实例化
export class Core
{
//私有的单例池对象
private static m_pPoolMgr:PoolMgr;
constructor()
{
//初始化创建单例对象
Core.m_pPoolMgr = new PoolMgr();
}
//对象初始化
public static init()
{
Core.m_pPoolMgr.init();
}
//外部获取单例对象
public static get PoolMgr():PoolMgr{
return Core.m_pPoolMgr;
}
//外部清理单例对象(切换账号,退出登陆等时调用)
public static clear()
{
Core.m_pPoolMgr.clear();
}
}
优点:
工具类变得更好找,直接输入core就可以智能提示有哪些工具类和对应的作用
缺点:
1:依旧缺乏一个统一的管理,每次创建一个新的工具都要写大量的代码
(看上面示例,每次创建一个工具类就要创建一个变量,并实现他的创建,初始化,获取,清理等方法,容易疏漏代码量太大)
四:进一步在一个类里面封装所有工具
这里是借鉴一个开源框架里面看到的思路,具体哪个框架我就忘记了
大致思路就是将所有工具做成单例存在一个字典里面,类名就是他的key,整个实现思路大概是下面这样的
1:首先要实现一个工具的接口类
export default abstract class IMgr {
public init(): void {
};
public clear(): void {
}
}
2:则工具类的实现变成了
//poolMgr.ts
export class PoolMgr implements IMgr{
//工具类初始化
public init()
{
}
public checkOut()
{
...
}
public checkIn()
{
...
}
//工具类重置
public clear()
{
}
}
3:实现一个获取单例的方法
export let _instanceMap = {};//单例存放容器
//获取某个对象的全局单例
export function getInst<T>(T): T {
let name = T.name;
if (_instanceMap[name]) return _instanceMap[name];
else {
let inst = new T();
_instanceMap[name] = inst;
return inst;
}
}
//对所有单例进行清理
export function clearInst(): void {
for (let name in _instanceMap) {
if (_instanceMap[name].clear) {
_instanceMap[name].clear();
}
}
}
4:则我们在Core实现工具的获取时只需要
//Core.ts
//工具入口类,所有工具都定义在这里
export class Core {
//只去初始化游戏开始就需要去初始化的工具
static init()
{
this.PoolMgr.init();
}
//===============所有工具都可以这样去获取=============
static get PoolMgr() {
return getInst<PoolMgr>(PoolMgr);
}
//==================================================
//清理所有工具类的数据
static clear()
{
clearInst();
}
}
优点:
1.所有工具集成到了一个类里面,工具名字不改,并且有了智能提示
2.所有工具类都被统一管理了,不需要去写额外的代码,避免失误造成的错误清理
3:懒汉式实例化单例,可以有效地减少内存的浪费
缺点:
因为是字典存取,会存在一定的消耗
当然并没有说这个方案就一定是最好,肯定还会有更好的方案,欢迎大家讨论交流,分享自己的方案,也欢迎大家关注我的公众号【数字媒体与游戏开发】