本帖最后由 剑寂万古 于 2023-6-5 17:32 编辑
对象池概述
主要用于减少创建对象的数量,以减少内存占用和提高性能。这种类型的设计模式属于结构型模式,它提供了减少对象数量从而改善应用所需的对象结构的方式。
介绍
意图:运用共享技术有效地支持大量细粒度的对象。
主要解决:在有大量对象时,有可能会造成内存溢出,我们把其中共同的部分抽象出来,如果有相同的业务请求,直接返回在内存中已有的对象,避免重新创建。
何时使用: 1、系统中有大量对象。 2、这些对象消耗大量内存。 3、这些对象的状态大部分可以外部化。 4、这些对象可以按照内部状态分为很多组,组内信息相对独立且对象状态可以重置。5、系统不依赖于这些对象身份,这些对象是不可分辨的。
优点:大大减少对象的创建,降低系统的内存,使效率提高。
缺点:1、不恰当的使用对象池可能导致系统中有大量无用对象占据内存空间.2、对象可能被污染,需要回收的时候重置一下状态。
使用场景: 1、系统有大量相似对象。 2、需要缓冲池的场景。
注意事项: 对象池必须有一个工厂对象加以控制。
简单对象池
class Item{
private state:number=0;
constructor(a,b,c,d,e){
}
public changeState(state:number){
this.state=state;
}
public setParam()
/**
*重置对象状态
*/
public reset(){
this.state=0;
}
}
//得到一个item对象
this.item=ItemPool.instance.spawn(a,b,c,d,e);
//回收一个对象
ItemPool.instance.unSpawn(this.item);
class ItemPool{
//单例方法
private static _instance:ItemPool;
public static get instance(){
return ItemPool._instance;
}
//存储列表
private pool:Item[]=[];
/**
*获得一个对象
*/
public spawn(...params):Item{
return this.pool.length>0?this.pool.pop():new Item(...params);
}
/**
*回收一个对象
*/
public unSpawn(item:Item){
item.reset();
this.pool.push(item);
}
}
- 缺点
- 对象工厂不可复用,每种对象对应一个对象工厂
- 对象需要保存,回收的时候需要记得调用对象工厂unSpawn方法,否则会内存泄漏
- 补充
- 针对忘记调用回收方法导致的内存泄漏,可以增加一个警告来避免,采取跟踪对象池对象数量的方式可以很容易的做到这一点
class ItemPool{
...
//当前对象数量
private currentCount:number=0;
//警告阈值
private warningCount:number=1000;
/**
*获得一个对象
*/
public spawn(...params):Item{
if(this.currentCount>this.warningCount){
//数量超限,发出警告
console.warning("对象数量超限");
}
return this.pool.length>0?this.pool.pop():{this.currentCount++,new Item(...params);}
}
...
}
- 如果存在潮汐对象,例如场景上动态创建1000个CUBE,但其中900个会在很短时间销毁且长时间不再复用,可以加入自动释放的机制
class ItemPool{
...
//释放阈值
private destoryCount:number=100;
/**
*回收一个对象
*/
public unSpawn(item:Item){
if(this.pool.length>this.destoryCount){
//达到释放阈值,直接释放
item.destroy();
}else{
//未达到释放阈值,回收到对象池
item.reset();
this.pool.push(item);
}
}
}
泛型对象池
interface Item {
/**
*重置对象状态
*/
reset();
}
class ItemA implements Item{
public reset(){
}
...
public methodA(){
}
}
class ItemB implements Item{
public reset(){
}
...
public methodB(){
}
}
const poolA=new ItemPool(ItemA);
poolA.spawn().methodA();
const poolB=new ItemPool(ItemB);
poolB.spawn().methodB();
class ItemPool<T extends Item>{
//泛型支持
constructor(private cls: { new(...params): T }) {
}
//存储列表
private pool: T[] = [];
/**
*获得一个对象
*/
public spawn(...params): T {
return this.pool.length > 0 ? this.pool.pop() : new this.cls(...params) as T;
}
/**
*回收一个对象
*/
public unSpawn(item: T) {
item.reset();
this.pool.push(item);
}
}
- 优点
- 加入了泛型支持,对象工厂可以共用一套代码
- 泛型代码提示可以提示子类的方法
- 缺点
- 需要有一个存储对象工厂的工厂
class ItemPoolFactory{
public static readonly PoolA=new ItemPool(A);
public static readonly PoolB=new ItemPool(B);
...
/**
*获取任意对象池对象
*/
private static getPool(cls:{ new(...params): T }){
return new ItemPool(cls);
}
}
- 对象需要保存,回收的时候需要记得调用对象工厂unSpawn方法,否则会内存泄漏
自回收对象池
interface Item {
/**
*重置对象状态
*/
reset();
/**
*回收对象
*/
destroy();
}
class ItemA implements Item {
//保存对象池实例用作回收
constructor(private pool: ItemPool<Item>) {
}
reset() {
}
/**
*销毁对象,这里实际是回收
*/
public destroy() {
this.reset();
this.pool.cache.push(this);
}
}
//得到一个item对象
this.item=ItemPool.instance.spawn(a,b,c,d,e);
this.item.destroy();
class ItemPool<T extends Item>{
//泛型支持
constructor(private cls: { new(pool: ItemPool<T>, ...params): T }) {
}
//存储列表
private pool: T[] = [];
public get cache() {
return this.pool;
}
/**
*获得一个对象
*/
public spawn(...params): T {
return this.pool.length > 0 ? this.pool.pop() : new this.cls(this, ...params) as T;
}
}
- 优点
- 加入了泛型支持,对象工厂可以共用一套代码
- 泛型代码提示可以提示子类的方法
- 回收方法在对象自己身上
- 缺点
- 需要有一个存储对象工厂的工厂
- 复杂的情况下可能出现循环引用
限定对象池
class Item{
constructor(a,b,c){
}
/**
*重置对象状态
*/
public reset(){
}
}
class ItemPool{
//单例方法
private static _instance:ItemPool;
public static get instance(){
return ItemPool._instance;
}
//对象索引
private cacheIndex:number=0;
//对象最大值
private cacheCount:number=10;
//存储列表
private pool:Item[]=[];
constructor(){
}
/**
*获得一个对象
*/
public spawn(...params):Item{
if(this.pool.length<this.cacheCount){
const item=new Item(...params);
this.pool.push(item);
return item;
}
if(this.cacheIndex==this.pool.length){
this.cacheIndex=0;
}
return this.pool[this.cacheIndex++];
}
/**
*回收一个对象
*/
public unSpawn(item:Item){
item.reset();
}
}
- 使用场景
- 针对限定数量的对象适用,例如全场特效数量控制
- 可以和前面的普通对象池使用方式结合
- 优点
- 限定了对象数量,性能上有保证
- 缺点
- 可能在对象使用时被复用,影响表现效果
享元对象池
class A{
private draw:Drawer;
constructor(){
this.draw=DrawerPool.getPool("A");
}
}
const a1=new A();
const a2=new A();
...
class B{
private draw:Drawer;
constructor(){
this.draw=DrawerPool.getPool("B");
}
}
class C{
private draw:Drawer;
constructor(){
this.draw=DrawerPool.getPool("C");
}
}
class Drawer{
}
class DrawerA extends Drawer{
//超级占内存的对象
private aBigMemoryObjectA;
constructor(){
}
}
class DrawerB extends Drawer{
//超级占内存的对象
private aBigMemoryObjectB;
constructor(){
}
}
class DrawerPool{
private pool:Map<string,Drawer>=new Map();
//获取对象池对象
public getPool(tag:string){
return this.pool.get(tag);
}
}
- 使用场景
- 将可复用的对象抽取为一个对象池。
- 优点
- 可以将占用内存较多的对象进行复用
- 缺点
- 享元对象(Drawer)中不能存在外部逻辑,不能保存状态,否则会出现污染
- 可能导致循环引用
|