享元模式在编辑器系统中大量的使用。
享元模式以共享的方式高效的支持大量的细粒度对象。
享元对象能够做到共享的关键是区分内部状态(Internal State)和外部状态(External State)。
内部状态是存储在享元对象内部并且不会随环境改变而改变。因此内部状态可以共享。
外部状态是随环境改变而改变的、不可以共享的状态。享元对象的外部状态必须由客户端保存,并且在享元对象被创建之后,在需要使用的时候再传入到享元对象的内部。
外部状态与内部状态是相互独立的。
享元模式设计的重点就在分离变与不变。把一个对象的状态分成内部状态和外部状态,内部状态是不变的,外部状态是可变的。然后通过共享不变的部分,达到减少对象数量并节约内存的目的。
分离变与不变是软件设计上最基本的方式之一,比如预留接口:一个常见的原因就是这里存在变化,可能在今后需要去扩展或者是改变已有的实现,因此预留接口作为“可插入性的保证”。
1、享元模式的本质:分离与共享
2、享元模式的优缺点:
优点:大幅度地降低内存中对象的数量,节省内存空间。
缺点:
(1)享元模式使得系统更加复杂。为了使对象可以共享,需要将一些状态外部化,这使得程序的逻辑复杂化。
(2)享元模式将享元对象的状态外部化,而读取外部状态使得运行时间变长。
3、在什么情况下(或满足哪些条件)使用享元模式:
(1)一个系统有大量对象
(2)这些对象耗费大量的内存
(3)这些对象的状态中的大部分都可以外部化
(4)这些对象可以按照内部状态分成很多组,当把外部对象从对象中剔除时,每一个组都可以仅用一个对象代替。
注意:使用享元模式需要维护一个记录了系统已有的所有享元的表,而这需要耗费资源。因此,应当在有足够多的享元实例可供共享时才值得使用享元模式。
享元模式模拟实现五子棋
UML图:
代码如下:
namespace 五子棋_享元模式 { abstract class AbstractChess { public abstract void play(Chess c); } } namespace 五子棋_享元模式 { class WhiteChess:AbstractChess { public override void play(Chess c) { Console.WriteLine("白子:" + "(" + c.X + "," + c.Y + ")"); } } } namespace 五子棋_享元模式 { class BlackChess:AbstractChess { public override void play(Chess c) { Console.WriteLine("黑子:" + "("+c.X + "," + c.Y + ")"); } } } namespace 五子棋_享元模式 { class ChessFactory { private Hashtable flyweights = new Hashtable(); public ChessFactory() { flyweights.Add("B", new BlackChess()); flyweights.Add("W", new WhiteChess()); } public AbstractChess GetChess(string key) { return ((AbstractChess)flyweights[key]); } } } namespace 五子棋_享元模式 { class Chess { private int x; private int y; public Chess() { } public Chess(int x, int y) { this.x = x; this.y = y; } public int X { get => x; set => x = value; } public int Y { get => y; set => y = value; } } } namespace 五子棋_享元模式 { class Program { static void Main(string[] args) { Chess c1 = new Chess(1, 1); Chess c2 = new Chess(2, 4); ChessFactory cf = new ChessFactory(); AbstractChess cf1 = cf.GetChess("B"); cf1.play(c1); AbstractChess cf2 = cf.GetChess("W"); cf2.play(c2); c1.X = 3; c1.Y = 4; cf1.play(c1); } } }