首页 > 其他分享 >对象池与享元模式

对象池与享元模式

时间:2024-03-15 20:46:07浏览次数:25  
标签:享元 对象 GameObject cache 模式 key go

在我人生的第一次面试中,面试官问我:

可以举几个例子吗?关于Unity中可能用到的设计模式。

我当时对设计模式的学习并没有很深入,因此回答了:对象池应该是享元模式。

但很快遭到了他的否决,我再次提议,应该是享元,但他还是摇了摇头。

毕竟是公司主程,我只能低下头表示,自己会回去看看。

在9个月之后的下午,公司项目中,我被分到的功能,需要使用尘封已久的对象池,我就心血来潮写下来这篇文章

来纠正我当时的错误

关于享元模式与对象池的区别

我比较喜欢举例子,其实两者的区别很大

区别一:对象的使用

比如-127~128的int,如果是享元模式,那当我们去取"0"的时候,就是把原原本本的0给你了;

如果是对象池,那当我们去取"0"的时候,会去池中看看我还有没有多余的"0",有就给你,没就new一个

区别二:对象的状态(内部与外部)

这就没有简单的例子了

比如租房,房子是内部,是不变的,中介是外部的,是你去租房时,传给房子的参数

如果是享元模式,当我们带笨笨中介去房源时,就会得到1000的房租,而狡猾中介是2333
当然内部数据也是能修改的,比如我给房子刷上超级油漆,那带不同外部中介去,就会得到不同房租

而对象池,你带中介去,他说房源有人正在看,给你现场造个房子,全是外部数据的影子

区别三:根本就不是一种存在

区别二里也看得出来,是不可能去现场造房子的

享元模式是一种数据处理用的模式,属于结构型模式,注重外部对象与内部对象之间的衔接。

对象池是一种对象使用方式,即构造型模式,注重对象的使用

相同点

都是为了优化内存而生的

3/15补充

今天又看到了一个好玩的例子

比如我这有一片树林,我准备创建一棵树。

如果是对象池,我会从我的仓库中拿出一颗树,更改它的属性后放在这。如果我的仓库中没有我想要的树先创建一棵树再拿出来。

如果是享元模式,我会找到树林的母树,再照着母树一颗树。
这时我之后的行为可分为两种:
一种是类似于使用不同颜色的画笔画树,以此得到不同的纸片(改变外部属性),
一种是类似于砍倒这棵母树(影响内部属性或改变行为)。
进行前者时一切正常,只有我操作的物体才会受到影响,
而后者则会让后来者再画树时,得到完全不一样的图案。

对象池源码

对象池单例类

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

interface IResetable
{
    void OnReset();
}
/*
 * 使用方法
 * 1.	所有频繁创建回收的物体,用对象池创建回收
 *      GameObjectPool.Instance.CreatObject("类型", 对象, 位置, 旋转);
 *      GameObjectPool.Instance.CollectObject(对象);
 * 2.	需要创建对象时执行,如果每次创建都需要对对象进行初始化,那就让对象实现IResetable接口。
 */
public class GameObjectPool : MonoSingleton<GameObjectPool>
{
    private Dictionary<string, List<GameObject>> cache;

    //初始化
    public override void Init()
    {
        base.Init();
        cache = new Dictionary<string, List<GameObject>>();
    }

    /// <summary>
    /// 为场景显示物体
    /// </summary>
    /// <param name="key">物体类别</param>
    /// <param name="prefab">物体预制体</param>
    /// <param name="pos">创建时位置</param>
    /// <param name="rotate">创建时角度</param>
    /// <returns></returns>
    public GameObject CreatObject(string key, GameObject prefab, Vector3 pos, Quaternion rotate)
    {
        GameObject go = FindUsableGO(key);
        if (go == null) go = AddObject(key, prefab);
        //设置物体并return
        go.transform.position = pos;
        go.transform.rotation = rotate;
        go.SetActive(true);
        foreach (var item in go.GetComponents<IResetable>())
        {
            item.OnReset();
        }
        return go;
    }

    //找到可用的物体
    private GameObject FindUsableGO(string key)
    {
        if (cache.ContainsKey(key))
            return cache[key].Find(g => !g.activeInHierarchy);
        return null;
    }

    //添加物体
    private GameObject AddObject(string key, GameObject prefab)
    {
        GameObject go = Instantiate(prefab);
        if (!cache.ContainsKey(key)) cache.Add(key, new List<GameObject>());
        cache[key].Add(go);
        return go;
    }

    /// <summary>
    /// 回收物体至对象池
    /// </summary>
    /// <param name="go">物体</param>
    /// <param name="delay">延迟时间</param>
    public void CollectObject(GameObject go, float delay = 0)
    {
        go.SetActive(false);
    }

    /// <summary>
    /// 移除该类别
    /// </summary>
    /// <param name="key">类别</param>
    public void Clear(string key)
    {   //如果用for,记得从后往前删,list会自动补上
        foreach (GameObject item in cache[key])
        {
            Destroy(item);
        }
        cache.Remove(key);
    }

    /// <summary>
    /// 移除对象池所有物体
    /// </summary>
    public void ClearAll()
    {
        //foreach 只读元素
        //遍历 字典 集合
        //foreach (KeyValuePair<string, List<GameObject>> item in cache)
        //{//异常:无效操作(因为把元素删了,IEnumerable需要用被删的那个元素去MoveNext())
        //    Clear(item.Key);//删除了字典记录,cache.Remove(key)
        //}

        //做法就是建一个新的,和删掉的元素不相干
        List<string> keys = new List<string>(cache.Keys);//之所以能赋值,是因为他们都有IEnumerable
        foreach (string item in keys)
        {
            Clear(item);//删了字典里的key,列表里的key没动。
        }
    }
}

标签:享元,对象,GameObject,cache,模式,key,go
From: https://www.cnblogs.com/LateUpdate/p/18076201

相关文章

  • 面向过程与面向对象你弄清楚了吗?
    1.面向过程与面向对象的区别面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了面向对象是把构成问题事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描述某个事务在整个解决问题的步骤中的行为......
  • 1分钟带你学会Python面向对象基础语法
    1.类和对象python中的面向对象主要学习类和对象类:是多个具有特殊功能的个体的集合,例如:人类/猫类/犬类对象:在一个类中,一个具有特殊功能的个体,能够帮忙解决某件特定的事情,也被称为实例两者之间的关系:类是用于描述某一类对象的共同特征,而对象是类的具体的存在在程序中......
  • MyBatis中经典的五种设计模式源码剖析,打死都不要忘记!
    MyBatis3.5版本中也广泛使用了多种设计模式,下面是其中一些主要使用的设计模式MyBatis一、构建器模式二、工厂模式三、代理模式四、模板方法模式五、装饰器模式六、代理模式一、构建器模式XMLConfigBuilder:用于解析MyBatis配置文件XMLMapperBuilder:用于解析......
  • Java访问者模式源码剖析及使用场景
    访问者模式一、介绍二、报表系统开发三、MyBatis中如何使用访问者模式?一、介绍Java中的访问者(Visitor)模式是一种行为型设计模式,它将数据结构与数据操作分离,使得在不修改数据结构的情况下可以增加新的操作。该模式主要包含以下几个角色:抽象访问者(Visitor):定......
  • 设计模式——抽象工厂实验
    抽象工厂实验实验场景:电子商务系统中创建的订单分为国内订单(DomesticOrder)和海外订单(OverseasOrder);国内订单使用人民币支付(RMBPayment),海外订单使用美元支付(USDPayment)。实验要求:设计使用抽象工厂模式来实现订单创建功能。实验内容:将订单工厂中的接口封装为order-api......
  • Java面向对象的一些学习笔记
    1.Private关键字:(1)private关键字是一个权限修饰符(2)可以修饰成员变量和成员方法(3)被private修饰的成员只能在本类中才能访问(4)针对private修饰的成员变量,如果需要被其他类使用,提供相应的操作(5)提供"setXxx(参数)"方法,用于给成员变量赋值,方法用public修饰(6)提供"getXxx(参数)......
  • 22_命令模式
    命令模式是一种行为设计模式,它将请求封装成一个对象,从而使不同的请求可以参数化其他对象,或者在不同的时间进行调用和执行。在这种模式中,命令对象充当中介者,负责将请求者和执行者进行解耦。命令模式包含以下几个角色:命令接口(CommandInterface):定义了命令对象的执行方法。具......
  • fastadmin命令行模式--command
    1.引入命令行文件fastadmin/application/command.php 2.在该位置新建执行文件此处以menu作说明protectedfunctionconfigure(){$this->setName('命令')->addOption('参数1','参数简称',Option::VALUE_REQUIRED(必填),'参数说明',默认值......
  • Spring IOC与工厂模式
    1.简单介绍在讲SpringIOC之前,有必要先来聊一下工厂模式(FactoryPattern)。工厂模式可将Java对象的调用者从被调用者的实现逻辑中分离出来。工厂模式是Java中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。在工厂模式中,我们在创建对象......
  • 21_迭代器模式
    迭代器模式是一种行为型设计模式,它提供了一种统一的方式来访问集合对象中的元素,而不需要暴露集合对象的内部结构。迭代器模式将遍历操作封装在迭代器对象中,使得客户端可以通过迭代器对象依次访问集合中的元素。迭代器模式有三个主要角色:迭代器(Iterator):定义了访问和遍历集合对......