【游戏设计模式】单例模式全解析---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的写法非常的冗余,没有人会选择此写法。

相关推荐
MinBadGuy28 分钟前
【GeekBand】C++设计模式笔记5_Observer_观察者模式
c++·设计模式
刷帅耍帅43 分钟前
设计模式-生成器模式/建造者模式Builder
设计模式·建造者模式
蜡笔小新..1 天前
【设计模式】软件设计原则——开闭原则&里氏替换&单一职责
java·设计模式·开闭原则·单一职责原则
性感博主在线瞎搞1 天前
【面向对象】设计模式概念和分类
设计模式·面向对象·中级软件设计师·设计方法
lucifer3111 天前
JavaScript 中的组合模式(十)
javascript·设计模式
lucifer3111 天前
JavaScript 中的装饰器模式(十一)
javascript·设计模式
蜡笔小新..1 天前
【设计模式】软件设计原则——依赖倒置&合成复用
设计模式·依赖倒置原则·合成复用原则
刷帅耍帅1 天前
设计模式-代理模式
设计模式·代理模式
神的孩子都在歌唱1 天前
行为设计模式 -观察者模式- JAVA
java·观察者模式·设计模式
刷帅耍帅2 天前
设计模式-解释器模式
设计模式·解释器模式