如何更好的统一管理工具类

前言:

大家在写业务的时候有没有出现过,想找一个工具,但又不知道有没有实现,想封装一个工具,又怕别人不知道,工具的实现老是容易存在重复造轮子的情况。

根据笔者多年的工作经验总结大概是因为以下几种原因造成的:

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:懒汉式实例化单例,可以有效地减少内存的浪费

缺点:

因为是字典存取,会存在一定的消耗

当然并没有说这个方案就一定是最好,肯定还会有更好的方案,欢迎大家讨论交流,分享自己的方案,也欢迎大家关注我的公众号【数字媒体与游戏开发】 :wink:

7赞

天哪,一个点赞评论的人都没有

我给你点个赞

1赞

我给你点个赞

1赞

非常感谢哥

爱你,萌萌哒

这个问题我之前在论坛给其他人说过,很简单


root/module/module_a.ts

// 内部引用的类型、枚举、接口等
namespace _module_a {
}

class module_a {
    // ...
}

// 外部引用的类型、枚举、接口等
export namespace module_a_ {
}

export default new module_a;

root/module/module_export.ts

export { default as module_a, module_a_ } from "./module_a";

root/module.ts

import * as module from "./module/module_export";
export default module;

外部使用只需要 module. 就能拿到所有集成的工具,集成方式按照工具分类来
我自己写的框架,工具类都是按照这种结构

2赞

挺好的,我刚毕业的第一家公司就是这样用的。但这并不是仅仅只是为了解决循环引用和查找的问题哦

循环引用?在我看来那是大部分是结构不合理,比如父引用子,
还有部分循环引用的,就比如工具类,但是实际上根据es6的规则只要不在模块加载时使用到并不会造成会循环引用的错误

如果是第一种,那我我很怀疑这个人的编程思想是否合格

如果是第二种,那么就不会有任何问题

1赞

如果还有问题。请举个例子

你的想法也挺好的,给你点赞

其实cocos需要一个类似npm package的代码白嫖管理分发平台

1赞

用第一种,然后放在同一个文件夹下,甚至加个一样的前缀(i.e. CRXXMgr,每次CR就能快速查找了)。至于循环引用的问题,大部分情况应该遇不到,遇到了稍微优化一下代码也能解决。

nice,这样也挺好的