【游戏设计模式】单例模式全解析---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,因此为了避免这种情况,才使用单例模式进行设计与开发。

基本原理:

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

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. 简易计数器

场景布局如下:

具体代码如下:

# 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();
    }
}

效果如下:

实现方式:

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

  1. 饿汉式-提前初始化

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

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

  1. 懒汉式-延迟初始化

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

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#实现泛型单例代码如下:

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实现泛型单例代码如下:

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的写法非常的冗余,没有人会选择此写法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ZZW游戏制造

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值