享元模式
- 设计模式
- 2024-07-06
- 617热度
- 0评论
一、概念
当系统存在大量相同或者相似的对象时,可以使用享元模式。享元模式通过共享技术实现相同或者相似的细粒度对象的复用,从而节约了内存空间,提高了系统性能。在享元模式中提供了一个享元池用于存储已经创建好的享元对象,并通过享元工厂类将享元对象提供给客户端使用。
定义:运用共享技术有效地支持大量细粒度对象的复用。
享元模式与对象池模式的区别:对象池的概念是为了避免频繁地进行对象创建和释放导致内存碎片,可以预先申请一片连续地区域空间,每次创建对象时,从对象池取出空闲对象来使用,使用完成后再放回对象池以供后续使用。
对象池复用为重复使用,主要目的是节省时间与性能消耗。而享元模式的复用可以理解为共享使用,整个声明周期都是被所有使用者共享的,主要目的是节省空间。
二、模式结构
享元模式(又称轻量级模式)通常结合工厂模式一起使用,要求能够被共享的对象必须是细粒度对象,是一种对象结构型模式。
-
Flyweight(抽象享元类):抽象享元类通常是一个接口或者是抽象类,其声明了具体享元类公共的方法,这些方法向外界提供享元对象的内部数据(内部状态),同时也通过这些方法来设置外部数据(外部状态)。
-
ConcreteFlyweight(具体享元类):具体享元类实现了抽象享元类,其实例被称为享元对象,为内部状态提供存储空间。
-
UnsharedConcreteFlyweight(非共享具体享元类):继承于抽象享元类,但是不能被共享。
-
FlyweightFactory(享元工厂类):用于创建并管理享元类对象,将各种类型的具体享元类对象存储在享元池中。
-
单纯共享模式:在单元共享模式中所有的具体享元类都是可以共享的,不存在非共享具体享元类。
-
复合享元模式:将一些单纯享元对象使用组合模式还可以形成复合享元对象,复合享元模式虽然本身不可共享,但是可以将多个内部状态不同的享元对象设置成相同的外部状态。
三、具体实现
设计一款围棋游戏,棋盘中存在需要除坐标不同,其他都相同的黑子、白子,如果将所有的棋子都作为一个独立对象存储在内存中,将导致所需内存空间较大,这个时候也使用享元模式来解决该问题。
- Coordinates:坐标类,用来表示享元模式的外部状态
public class Coordinates
{
private int x;
private int y;
public Coordinates(int x, int y)
{
this.x = x;
this.y = y;
}
public int getX()
{
return x;
}
public int getY()
{
return y;
}
public void setPos(int x, int y)
{
this.x = x;
this.y = y;
}
}
- IgoChessman(抽象享元类):抽象棋子类
public abstract class IgoChessman
{
public abstract string GetColor();
public void Display(Coordinates coordinates)
{
Console.WriteLine($"棋子颜色: {GetColor()}, 棋子位置: {coordinates.getX()}, {coordinates.getY()}");
}
}
- BlackIgoChessman、WhiteIgoCheesman(具体享元类):黑白棋子类
public class BlackIgoChessman : IgoChessman
{
public override string GetColor()
{
return "Black";
}
}
public class WhiteIgoCheesman : IgoChessman
{
public override string GetColor()
{
return "White";
}
}
- IgoChessmanFactory(享元工厂类):创造黑白棋子
public class IgoChessmanFactory
{
private static IgoChessmanFactory _instance;
private Dictionary<string, IgoChessman> _igoChessDictionary = new Dictionary<string, IgoChessman>();
public static IgoChessmanFactory GetInstance()
{
if (_instance == null) _instance = new IgoChessmanFactory();
return _instance;
}
public IgoChessman GetIgoChessman(string name)
{
if (!_igoChessDictionary.TryGetValue(name, out IgoChessman chessman))
{
if (name == "Black") chessman = new BlackIgoChessman();
else if (name == "White") chessman = new WhiteIgoCheesman();
_igoChessDictionary.Add(name, chessman);
}
return chessman;
}
}
- Client:客户端
IgoChessman black1, black2, black3, white1, white2;
IgoChessmanFactory igoChessmanFactory = IgoChessmanFactory.GetInstance();
Coordinates coordinates = new Coordinates(0, 0);
black1 = igoChessmanFactory.GetIgoChessman("Black");
black2 = igoChessmanFactory.GetIgoChessman("Black");
black3 = igoChessmanFactory.GetIgoChessman("Black");
if (black1 == black2 && black2 == black3) Console.WriteLine("Same Black Chess");
white1 = igoChessmanFactory.GetIgoChessman("White");
white2 = igoChessmanFactory.GetIgoChessman("White");
if (white1 == white2) Console.WriteLine("Same White Chess");
black1.Display(coordinates);
coordinates.setPos(1, 0);
black2.Display(coordinates);
coordinates.setPos(1, 1);
black3.Display(coordinates);
coordinates.setPos(1, 2);
white1.Display(coordinates);
coordinates.setPos(1, 3);
white2.Display(coordinates);
四、优缺点
- 优点:
- 降低内存消耗:通过共享内部状态,相同对象只需要保存一份,从而减少了系统对象的数量,降低内存占用。
- 提高性能:由于对象数量减少,性能也会相应提升。
- 缺点:
- 增加程序复杂性:为了使对象可以共享,需要将不能共享的状态外部化,使逻辑变得复杂。
- 运行时间变长:读取享元模式的外部状态可能会增加运行时间。
五、适用场景
- 当系统有大量相同或者相似的对象,造成内存浪费时,可以使用享元模式。
- 对象的大部分状态都可以外部化,可以将这些外部状态传入对象。