一、什么是享元模式
使用共享对象可有效地支持大量的细粒度的对象
主要用于减少创建对象,以减少内存占用和提高性能。是对象池的一种实现,避免不停地创建对象销毁对象,把对象状态分为内部状态和外部状态,内部状态是共享不变的,外部状态是可改变的。
UML图
Flyweight: 是所有的具体享元类的基类,为具体享元规范需要实现的公共接口,非享元的外部状态以参数的形式通过方法传入。
ConcreteFlyweight:具体的享元类,更丰富的细节
FlyweightFactory:负责创建和管理享元角色。当客户对象请求一个享元对象时,享元工厂检査系统中是否存在符合要求的享元对象,如果存在则提供给客户;如果不存在的话,则创建一个新的享元对象。
二、适用场景
缓冲池,围棋下子,敌方NPC。系统中存在大量的相似对象细粒度的对象都具备较接近的外部状态,而且内部状态与环境无关,也就是说对象没有特定身份,需要缓冲池的场景。比如FPS模式中射出去的子弹,不用的子弹可以回收的,再次发射就是已经实例的子弹。
三、优缺点
优点
大大减少对象创建,节省内存空间和资源
缺点
为了使对象可以共享,需要将一些不能共享的状态外部化,这将增加程序的复杂性。
读取享元模式的外部状态会使得运行时间稍微变长。
四、大话中的例子
创建网站的例子,有许许多多个人博客和陈列网页,利用享元模式,可以节省内存空间。建立一个网站抽象类,定义公共接口和外部参数。用户类存放用户名信息,具体的网站类实现公共接口,并接受外部信息,也就是用户信息。一个网站工厂类,可获得网站实例,如果不存在实例则实例网站,如果存在了就不必实例了。
五、我的例子
using System;
using System.Collections;
using System.Reflection;
namespace FlyweightMode
{
class Program
{
static void Main(string[] args)
{
NinjutsuFactory ninjutsus = new NinjutsuFactory();
Ninjutsu ninjutsu1 = ninjutsus.CreateNinjutsu("Rasengan");
ninjutsu1.TakeNinjutsu(new Ninjar("鸣人"), 100);
Ninjutsu ninjutsu2 = ninjutsus.CreateNinjutsu("Rasengan");
ninjutsu2.TakeNinjutsu(new Ninjar("自来也"), 160);
Ninjutsu ninjutsu3 = ninjutsus.CreateNinjutsu("Rasengan");
ninjutsu3.TakeNinjutsu(new Ninjar("水门"), 220);
Ninjutsu ninjutsu4 = ninjutsus.CreateNinjutsu("Kagebunsin");
ninjutsu4.TakeNinjutsu(new Ninjar("卡卡西"), 2);
Ninjutsu ninjutsu5 = ninjutsus.CreateNinjutsu("Kagebunsin");
ninjutsu5.TakeNinjutsu(new Ninjar("鸣人"), 1000);
Console.WriteLine("数量:" + ninjutsus.GetNinjusuNum());
Console.ReadKey();
}
}
/// <summary>
/// 忍术
/// </summary>
abstract class Ninjutsu
{
protected string name;
/// <summary>
/// 施展技能
/// </summary>
/// <param name="ninjar">谁施展技能</param>
/// <param name="para">技能参数</param>
public abstract void TakeNinjutsu(Ninjar ninjar, int para);
}
/// <summary>
/// 螺旋丸
/// </summary>
class Rasengan : Ninjutsu
{
public Rasengan()
{
name = "螺旋丸";
}
public override void TakeNinjutsu(Ninjar ninjar, int para)
{
Console.WriteLine("{0}使用了{1}", ninjar.Name, name);
Console.WriteLine("造成{0}点伤害.", para);
}
}
/// <summary>
/// 隐分身
/// </summary>
class Kagebunsin : Ninjutsu
{
public Kagebunsin()
{
name = "隐分身术";
}
public override void TakeNinjutsu(Ninjar ninjar, int para)
{
Console.WriteLine("{0}使用了{1}", ninjar.Name, name);
Console.WriteLine("增加{0}个分身", para);
}
}
/// <summary>
/// 忍者,存个名字
/// </summary>
public class Ninjar
{
string name;
public Ninjar(string name)
{
this.name = name;
}
public string Name { get => name; }
}
/// <summary>
/// 忍术工厂
/// </summary>
class NinjutsuFactory
{
Hashtable ninjutsus = new Hashtable();//哈希表,存已经实例的忍术的
/// <summary>
/// 生成忍术
/// </summary>
/// <param name="key">忍术关键词</param>
/// <returns></returns>
public Ninjutsu CreateNinjutsu(string key)
{
if (!ninjutsus.ContainsKey(key))
{
var asmb = Assembly.GetExecutingAssembly();//使用反射,获得当前程序集
var t = asmb.CreateInstance("FlyweightMode." + key);//程序集通过类的完全限定名实例
ninjutsus.Add(key, t);//存储起来
}
return (Ninjutsu)ninjutsus[key];
}
/// <summary>
/// 获得实例个数
/// </summary>
/// <returns></returns>
public int GetNinjusuNum()
{
return ninjutsus.Count;
}
}
}
运行结果
PS:享元模式,其实是缓冲池的运用, 关键在于将细粒度不同的对象共享为同一对象,节省内存资源。