对象池系列(一)
对象池的核心思想:预先初始化一组可重用的实体,而不是按需销毁然后重建。每次要用的时候都去创建用完就销毁掉,会造成频繁的资源回收;
首先应该有基本的两个方法生产Spawn回收Despawn.假如我们需要对子弹Bullet进行Spawn和Despawn处理。于是我们定义了一个IPool接口和Pool类
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public interface IPool
{
Bullet Spwan();
void Despawn(Bullet bullet);
}
public class Pool : IPool
{
private Stack<Bullet> cacheStack = new Stack<Bullet>();
public Bullet Spwan()
{
return cacheStack.Count > 0 ? cacheStack.Pop() : new Bullet();
}
public void Despawn(Bullet bullet)
{
cacheStack.Push(bullet);
}
}
最基本的Bullet对象池就创建好了,那么来看看有什么问题?1.对于扩展极不方便,如果需要增加一个其他类似于子弹这样的对象池,我们还需要去增加额外的例如IPool和Pool这样的脚本,所以这里想到的应该是利用泛型。代码如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public interface IPool<T>
{
T Spwan();
void Despawn(T obj);
}
public class Pool<T> : IPool<T>
{
private Stack<T> cacheStack = new Stack<T>();
public T Spwan()
{
return cacheStack.Count > 0 ? cacheStack.Pop() : default(T);
}
public void Despawn(T obj)
{
cacheStack.Push(obj);
}
}
到这里我们解决了扩展的问题。我们再来看看还有什么问题?假如我创建子弹的时候需要额外的参数应该怎么办?这里的Default(T)就不在适用了,这里到底是在干什么?我们首先得弄清楚,在上一个版本中这里是new Bullet()对象,而现在这里是给一个默认的对象。总而言之这里是在创建一个新的对象。根据功能来看这里需要返回一个T类型的对象,但是具体返回一个什么样的对象,我们是不知道的,也就是说这里的细节应该在于子类去完成,所以这里应该抽象化(细节依赖于抽象)。首先我想到的就是接口,其次这里的功能是创建对象,提到"创建"我就想到了工厂。所以这里增加了一个IFactory这样的Interface.代码如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public interface IPool<T>
{
T Spwan();
void Despawn(T obj);
}
public interface IFactory<T>
{
T Create();
}
public class Pool<T> : IPool<T>
{
private Stack<T> cacheStack = new Stack<T>();
private IFactory<T> factory;
public T Spwan()
{
return cacheStack.Count > 0 ? cacheStack.Pop() : factory.Create();
}
public void Despawn(T obj)
{
cacheStack.Push(obj);
}
}
我们再来看看如何,Pool声明了一个IFactory的引用factory,而原来的Default(T)被替换成了factory.Create().到了这里唯一没有确定的就是facotry该如何传进来,首先想到的就是构造器。代码如下:增加了一个构造函数需要传进来一个IFactory的对象。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public interface IPool<T>
{
T Spwan();
void Despawn(T obj);
}
public interface IFactory<T>
{
T Create();
}
public class Pool<T> : IPool<T>
{
private Stack<T> cacheStack = new Stack<T>();
private IFactory<T> factory;
public Pool(IFactory<T> factory)
{
this.factory = factory;
}
public T Spwan()
{
return cacheStack.Count > 0 ? cacheStack.Pop() : factory.Create();
}
public void Despawn(T obj)
{
cacheStack.Push(obj);
}
}
到了这里可以算是初步完成了,但是我们再来思考一下,如果我每次增加一个例如类似于BulletPool这样的对象池,我还需要额外增加一个实现了IFacotry接口的类,这样增加了客户端的使用麻烦。所以我这个时候就想到了怎么样才能把额外增加的实现了IFacotry接口的类省去,于是我们再来看看IFactory这个接口到底做了什么事情?原来如此,它就是负责一件事情,对象的创建!!!所以这里我们可以把factory对象的创建转移到内部。代码如下:
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public interface IPool<T>
{
T Spwan();
void Despawn(T obj);
}
public interface IFactory<T>
{
T Create();
}
public class ObjectFactory<T> : IFactory<T>
{
private Func<T> _createMethod;
public ObjectFactory(Func<T> createMethod)
{
_createMethod = createMethod;
}
public T Create()
{
return _createMethod();
}
}
public class Pool<T> : IPool<T>
{
private Stack<T> cacheStack = new Stack<T>();
private IFactory<T> factory;
public Pool(Func<T> createObjectMethod)
{
factory = new ObjectFactory<T>(createObjectMethod);
}
public T Spwan()
{
return cacheStack.Count > 0 ? cacheStack.Pop() : factory.Create();
}
public void Despawn(T obj)
{
cacheStack.Push(obj);
}
}
这样factory对象的创建就转移到了内部,客户端不再需要去额外增加类。到这里差不多就算完成了。但是我们再来看看还有没有需要完善的地方,果真如此。这里这个Despawn()方法再我看来还需要修改修改,比如Unity中GameObject我在回收掉之前想做一些特殊处理,例如gameObject.SetActive(false)之类的操作。我们发现对于不同对象的回收,他们可能都有着不一样的地方,对于不一样的地方,那么就应该抽象。所以我认为这里可以将Despawn这个方法抽象化,也就是实现为抽象方法。代码如下:
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public interface IPool<T>
{
T Spwan();
void Despawn(T obj);
}
public interface IFactory<T>
{
T Create();
}
public class ObjectFactory<T> : IFactory<T>
{
private Func<T> _createMethod;
public ObjectFactory(Func<T> createMethod)
{
_createMethod = createMethod;
}
public T Create()
{
return _createMethod();
}
}
public abstract class Pool<T> : IPool<T>
{
protected Stack<T> cacheStack = new Stack<T>();
protected IFactory<T> factory;
public T Spwan()
{
return cacheStack.Count > 0 ? cacheStack.Pop() : factory.Create();
}
public abstract void Despawn(T obj);
}
public class ObjectPool<T> : Pool<T>
{
protected Action<T> _resetMethod;
public ObjectPool(Func<T> createMethod,Action<T> resetMethod)
{
factory = new ObjectFactory<T>(createMethod);
_resetMethod = resetMethod;
}
public override void Despawn(T obj)
{
_resetMethod?.Invoke(obj);
cacheStack.Push(obj);
}
}
public class BulletPoolManager : MonoBehaviour
{
public ObjectPool<Bullet> BulletPool;
public GameObject BulletPrefab;
private void Awake()
{
BulletPool = new ObjectPool<Bullet>(() => Instantiate(BulletPrefab).GetComponent<Bullet>(), bullet => bullet.gameObject.SetActive(false));
}
}