【基础知识】
如果不知道什么是对象池,请先了解下。
关于对象池,我们需要知道池子里的最大容量和当前可用的对象数量;需要从对象池里获取和回收对象;随着生成的对象越来越多,池子会扩容,要有扩容的操作和策略,反之要有缩容的操作和策略。我们可以根据这些来定义简单的接口和相关枚举:
using System.Collections;
using System.Collections.Generic;
using System;
namespace Cache
{
public interface ICountable
{
//数量和容量
int Count();
int Capacity();
}
public interface IShrinkable
{
bool Shrink(ShrinkStrategy shrinkStrategy, float percent,int count, bool weedout);//WeedOut是指缩容时是否强行淘汰对象
}
public interface ICache<T>:ICountable,IShrinkable,IDisposable
{
//获取和放入,一一对应
bool Get(out T value);
bool Put(T value);
//扩容和缩容,一一对应,自动扩容,手动缩容
bool Expansion();
}
public enum ExpansionStrategy
{
NONE = 0,//不扩容
FIXEDCOUNT,//固定数量
DOUBLE,//双倍
PERCENT,//一定比例
}
public delegate void WeedOutDelegate<T> (T value);
public enum ShrinkStrategy
{
NONE = 0,
FIXEDCOUNT,
DOUBLE,
PERCENT,
}
}
可以看到没有定义一个大而全的接口,反而是定义了很多小的接口 ,这是因为大而全的接口可以拆分为ABC等部分,而另外的某个功能也会用到这些部分。
实现和注释请看代码
【代码实现】
using System;
using System.Collections;
using System.Collections.Generic;
namespace Cache
{
public class FIFO<T> : ICache<T>
{
private Queue<T> queue;//直接用队列,也可以自己用链表实现
private int capacity;
private ExpansionStrategy expansionStrategy;
private WeedOutDelegate<T> weedOutDelegate;
private bool disposed;
/// <summary>
/// 构造器
/// </summary>
/// <param name="capacity"></param>
/// <param name="expansionStrategy">默认没有扩容</param>
/// <param name="weedOutDelegate"></param>
public FIFO(int capacity, ExpansionStrategy expansionStrategy = ExpansionStrategy.NONE, WeedOutDelegate<T> weedOutDelegate = null)
{
this.capacity = capacity;
this.weedOutDelegate = weedOutDelegate;
this.expansionStrategy = expansionStrategy;
queue = new Queue<T>(capacity);
}
public int Capacity()
{
return capacity;
}
public int Count()
{
return queue.Count;
}
public bool Get(out T value)
{
return queue.TryDequeue(out value);
}
public bool Put(T value)
{
if (!value.GetType().IsValueType && default(T) == null)//如果是引用类型,且为空,要返回false,因为这些操作的返回类型不能是void,而要是bool
{
return false;
}
if (queue.Count == capacity)//如果当前的数量等于容量,那么要先扩容
{
if (!Expansion())
{
if(weedOutDelegate != null)
{
weedOutDelegate(value);//如果扩容失败,将这个对象淘汰
}
else//否则Put 失败
{
return false;
}
}
}
queue.Enqueue(value);
return true;
}
public bool Expansion()
{
bool result = true;
switch(expansionStrategy)
{
case ExpansionStrategy.NONE: result = false;break;
case ExpansionStrategy.FIXEDCOUNT: ExpansionWithFixedCount(3);break;//可以把参数放到构造器中,这里省略不写了
case ExpansionStrategy.DOUBLE:capacity *= 2;break;
case ExpansionStrategy.PERCENT: ExpansionWithPercent(0.3f);break;
}
return result;
}
private void ExpansionWithFixedCount(int count)
{
if (count < 0)
count = 0;
capacity += count; //使用的默认队列结构,会自动扩容,只要增加容量即可
}
private void ExpansionWithPercent(float value)
{
value = Math.Clamp(value, 0f, 1f);
capacity = (int)(capacity * (1 + value ));
}
//扩容自动,缩容由外部调用
public bool Shrink(ShrinkStrategy shrinkStrategy,float percent = 0, int count = 0,bool weedout = false)
{
bool result = true;
switch(shrinkStrategy)
{
case ShrinkStrategy.NONE:break;
case ShrinkStrategy.FIXEDCOUNT:
{
int residualCapacity = capacity - count;
if (residualCapacity < 0)//容量不能小于0
{
result = false;
break;
}
if (residualCapacity >= queue.Count)
{
capacity = residualCapacity;
}
else
{
if (weedout)//淘汰
{
int num = queue.Count - residualCapacity;
while (num > 0)
{
result = WeedOut();
num--;
}
capacity = residualCapacity;
}
else
{
capacity = queue.Count;
}
}
break;
}
case ShrinkStrategy.DOUBLE:
count = capacity / 2;
Shrink(ShrinkStrategy.FIXEDCOUNT,percent, count, weedout);
break;
case ShrinkStrategy.PERCENT:
count = (int)(capacity * percent);
Shrink(ShrinkStrategy.FIXEDCOUNT, percent, count, weedout);
break ;
}
return result;
}
private bool WeedOut()
{
if (queue.Count == 0)//做个保护,虽然不会出现这种情况
return false;
T value = queue.Dequeue();
weedOutDelegate?.Invoke(value);
return true;
}
~FIFO()
{
this.Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(true);
}
protected virtual void Dispose(bool dispose)
{
if(disposed)
{
return;
}
if(dispose)
{
//这个不用处理非托管资源,用委托传递出去
}
foreach (var item in queue)
{
weedOutDelegate?.Invoke(item);
}
queue.Clear();
disposed = true;
}
}
}