【游戏设计模式】单例模式全解析---Cocos Creater实操

前言

七大原则:

  • 单一职责原则(Single Responsibility Principle);

  • 开闭原则(Open Closed Principle);

  • 里氏替换原则(Liskov Substitution Principle);

  • 迪米特法则(Law of Demeter);

  • 接口隔离原则(Interface Segregation Principle);

  • 依赖倒置原则(Dependence Inversion Principle);

  • 合成复用原则(Composite Reuse Principle);

只有一个目的:

让程序员可以更好的偷懒,让所写过的代码可以应对各种各样的需求。

定义:

单例模式是一种创建型 设计模式,它确保一个类只有一个实例 ,并提供了一个全局访问点来访问该实例。

应用场景:

全局只想要一个实例对象,避免多个实例对象会存在数据不统一等情况下使用,具体场景如下:

  1. 全局游戏管理-GameManager:管理游戏运行时的状态

  2. 全局音频管理-AudioManager:管理游戏运行时的背景音乐、音效、人声等

  3. 全局数据代理-ProxyManager:管理游戏数据

例如全局音频管理播放了一个BGM,如果存在多个音频管理,就会导致多个BGM存在,一般情况游戏只会有一个BGM,因此为了避免这种情况,才使用单例模式进行设计与开发。

基本原理:

将类的初始化改成私有,只能通过自身进行初始化,这样就避免其他地方初始化多个对象出来。

typescript 复制代码
import { _decorator } from 'cc';
const { ccclass } = _decorator;

@ccclass('GameManager')
export class GameManager {
    private static _instance: GameManager = null;

    public static get Instance() {
        // 只能通过自身进行初始化
        if (this._instance == null) {
            this._instance = new GameManager();
        }

        return this._instance;
    }

    // 类的初始化改为私有
    private constructor() {}
}

具体案例:

  1. 简易计数器

场景布局如下:

具体代码如下:

typescript 复制代码
# GameManager.ts
import { _decorator } from 'cc';
const { ccclass } = _decorator;

// 全局游戏管理
@ccclass('GameManager')
export class GameManager {
    private static _instance: GameManager = null;

    public count: number = 0;

    // 只能通过自身进行初始化
    public static get Instance() {
        if (this._instance == null) {
            this._instance = new GameManager();
        }

        return this._instance;
    }

    // 类的初始化改为私有
    private constructor() {

    }
}

# SingletonExampleScene.ts
import { _decorator, Component, Node, Button, Label } from 'cc';
import { GameManager } from './GameManager';
const { ccclass, property } = _decorator;

@ccclass('SingletonExampleScene')
export class SingletonExampleScene extends Component {
    @property(Button)
    private AddButton: Button = null;

    @property(Button)
    private ReduceButton: Button = null;

    @property(Label)
    private textLabel: Label = null;

    public onLoad(): void {
        this.AddButton.node.on(Button.EventType.CLICK, this.OnClick_AddButton, this);
        this.ReduceButton.node.on(Button.EventType.CLICK, this.OnClick_ReduceButton, this);

        this.RefreshTextLabel();
    }

    private OnClick_AddButton(button: Button): void {
        GameManager.Instance.count++;
        this.RefreshTextLabel();
    }

    private OnClick_ReduceButton(button: Button): void {
        GameManager.Instance.count--;
        this.RefreshTextLabel();
    }

    private RefreshTextLabel(): void {
        this.textLabel.string = GameManager.Instance.count.toString();
    }
}

效果如下:

实现方式:

这里暂时只讲两种类型(不考虑线程安全,游戏中多线程的情况比较少),分别是提前初始化和延迟初始化

饿汉式-提前初始化

以GameManager为例,该实现方式会在程序启动时在静态存储区初始化GameManager中的静态变量_instance。

typescript 复制代码
import { _decorator } from 'cc';
const { ccclass } = _decorator;

@ccclass('GameManager')
export class GameManager {
    private static _instance: GameManager = new GameManager();

    public static get Instance() {
        return this._instance;
    }

    private constructor() {

    }
}

优点(不考虑线程安全):无

缺点:

  1. 程序启动时就直接全部初始化完成,占用大量内存,有可能有些单例对象从头到尾都没使用过,却一直占据程序内存。

懒汉式-延迟初始化

以GameManager为例,该实现方式只在第一次调用时初始化GameManager中的静态变量_instance。

typescript 复制代码
import { _decorator } from 'cc';
const { ccclass } = _decorator;

@ccclass('GameManager')
export class GameManager {
    private static _instance: GameManager = null;

    public static get Instance() {
        // 只能通过自身进行初始化
        if (this._instance == null) {
            this._instance = new GameManager();
        }

        return this._instance;
    }

    // 类的初始化改为私有
    private constructor() {}
}

优点:

  1. 调用才会初始化,不会一开始就占据程序内存。

缺点(不考虑线程安全):无

总结:

优点:

  1. 在内存中只有一个实例对象,不会过多创建对象,保证数据统一性。
  2. 避免对资源的多重占用。

缺点:

  1. 由于不能多态(进行继承),导致可拓展性差,所有功能都只能写在一个类统一管理。

题外话:

作者打算实现TypeScript的泛型单例模式时,查阅资料发现,TypeScript无法像C#那样简单的写出泛型单例模式。

C#实现泛型单例代码如下:

csharp 复制代码
public class Singleton<T> where T : class, new()
{
    private static T s_instance;
    
    // 类的初始化改为私有或保护
    protected Singleton()
    {
    }

    public static T Instance
    {
        get
        {
            if (Singleton<T>.s_instance == null)
            {
                Singleton<T>.CreateInstance();
            }
            return Singleton<T>.s_instance;
        }
    }
}

// 使用方式
GameManager.Instance.Count++;

TypeScript实现泛型单例代码如下:

typescript 复制代码
export class Singleton<T>{
    private static _instance: any = null;

    protected constructor() {}

    public static GetInstance<T>(target: { new(): T }): T {
        if (this._instance == null) {
            this._instance = new target();
        }
        return this._instance;
    }
}

// 使用方式
GameManager.Instance<GameManager>(GameManager).Count++;

很明显TypeScript的写法非常的冗余,没有人会选择此写法。

相关推荐
苏渡苇23 分钟前
优雅应对异常,从“try-catch堆砌”到“设计驱动”
java·后端·设计模式·学习方法·责任链模式
短剑重铸之日1 小时前
《设计模式》第十一篇:总结
java·后端·设计模式·总结
feasibility.2 小时前
AI 编程助手进阶指南:从 Claude Code 到 OpenCode 的工程化经验总结
人工智能·经验分享·设计模式·自动化·agi·skills·opencode
BD_Marathon2 小时前
七大设计原则介绍
设计模式
YigAin5 小时前
Unity23种设计模式之 享元模式
设计模式·享元模式
范纹杉想快点毕业18 小时前
实战级ZYNQ中断状态机FIFO设计
java·开发语言·驱动开发·设计模式·架构·mfc
茂桑1 天前
DDD领域驱动设计-基础设施层
设计模式·架构
小温冲冲1 天前
通俗且全面精讲工厂设计模式
设计模式
进击的小头1 天前
设计模式与C语言高级特性的结合
c语言·设计模式
小温冲冲1 天前
通俗且全面精讲单例设计模式
开发语言·javascript·设计模式