第二十二章 享元模式(Flyweight Pattern)
意图
运用享元技术有效的支持大量细粒度的对象,共享时指在同一个场景中被多次引用而不发生变化,即共享对象的是无状态并与上下文无关的。
......
意图
运用享元技术有效的支持大量细粒度的对象,共享时指在同一个场景中被多次引用而不发生变化,即共享对象的是无状态并与上下文无关的。
使用场合
1.一个应用程序使用了大量的对象。
2.完全由于使用大量的对象造成很大的存储开销。
3.对象的大多数状态都可以变为外部状态。
4.如果删除对象的外部状态,那么可以用相对较少俄共享对象取代很多组对象。
5.应用程序不依赖对象标识,由于享元对象可以被共享,所以对于概念上明显有别的对象,标识测试将返回真值。
结构
享元对象在实现上使用了注册工厂,由一个享元工厂负责管理共享对象。当用户请求享元对象时,工厂首先查找该对象是否已注册。如果注册,则返回;否则创建一个新对象,将其注册并返回。
享元的难点在于如何分离对象的动态特征,并且尽量不用保存这些特征。因为动态特征仍需要大量的对象来保存,会使享元的模式的效果大打折扣。
效果
享元节省了大量的内存,代价是适用享元时需要查找,从而增加了处理时间。
例子:围棋围棋由棋盘和棋子组成,棋子有黑子和白子。如果将棋子看成对象,其静态特征是颜色,动态特征是位子和步数。棋盘上最多有361个棋子。如果每个棋子都是一个对象,那么实例的数量将会非常多。因此我们需要减少棋子的对象。
这种情况适合享元的如下使用场合:
1.围棋中需要使用大量的细粒度对象,即棋子。
2.棋子需要反复被使用,从而导致大量的开销。
3.对象的内部状态很简单,外部状态容易分离。
4.不考虑外部状态,只用黑子和白子两个对象既可表示。
5.棋盘上的棋子没有区别,即不依赖对象标识。
Board
using System;
using System.Windows.Forms;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Collections;
namespace I_go.AppCode
{
/**//// <summary>
/// 棋盘
/// </summary>
class Board
{
private int _width;
//保存棋子
private ArrayList stones;
public int Width
{
get { return _width;}
set { _width=value;}
}
private Graphics _grap;
public Board(Graphics g)
{
stones = new ArrayList();
this._grap = g;
}
//清空棋盘
public void Clear()
{
stones.Clear();
Draw();
}
public Graphics Grap
{
get { return _grap; }
}
private void AddStone(int x,int y)
{
string l=x.ToString("00")+y.ToString("00");
//判断该位置是否已有棋子
foreach (string l1 in stones)
{
if (l1 == l)
{
return;
}
}
//画棋子
stones.Add(l);
Stone s = StoneFactory.Instance().GetStone(stones.Count);
s.Draw(x, y, this);
}
//根据鼠标事件添加棋子
public void AddStone(MouseEventArgs e)
{
int w = _width / 20;
int x = (e.X + w / 2) / w;
int y = (e.Y + w / 2) / w;
if (x < 1 || x > 19 || y < 1 || y > 19)
{
return ;
}
AddStone(x,y);
}
//绘制棋盘和棋子
public void Draw()
{
int w = _width / 20;
Brush b = new SolidBrush(Color.Chocolate);
_grap.FillRectangle(b,0,0,_width,_width);
Pen p = new Pen(Color.Black);
for (int i = 1; i <= 19; i++)
{
_grap.DrawLine(p, i * w, w, i * w, 19 * w);
_grap.DrawLine(p, w, i * w, 19 * w, i * w);
}
for (int i = 4; i <= 16; i = i + 6)
{
for (int j = 4; j <= 16; j = j + 6)
{
_grap.DrawRectangle(p, i * w - 1, j * w - 1, 2, 2);
}
}
DrawStones();
}
public void DrawStones()
{
for (int i = 0; i < stones.Count;i++ )
{
string l=stones[i].ToString();
int x, y;
x = int.Parse(l.Substring(0,2));
y = int.Parse(l.Substring(2, 2));
Stone s = StoneFactory.Instance().GetStone(i+1);
s.Draw(x,y,this);
}
}
}
}
Stone
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
namespace I_go.AppCode
{
/**//// <summary>
/// 棋子
/// </summary>
class Stone
{
private Color color;
public Stone(Color c)
{
color=c;
}
//在棋盘上画棋子
//Board是棋盘对象
public void Draw(int x, int y, Board board)
{
Graphics g = board.Grap;
int w = board.Width / 20;
int x0, y0, r;
x0 = x * w - w / 2;
y0 = y * w - w / 2;
r = w / 2 + w / 2;
Brush b = new SolidBrush(this.color);
g.FillEllipse(b,x0,y0,r,r);
}
}
}
StoneFactory
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
namespace I_go.AppCode
{
/**//// <summary>
/// 用于创建黑白子
/// </summary>
class StoneFactory
{
private Stone blackstone;
private Stone whitestone;
"将棋子工厂定义为Singleton"#region "将棋子工厂定义为Singleton"
private static StoneFactory StoneFactory_stonefactory = null;
private StoneFactory()
{
blackstone = new Stone(Color.Black);
whitestone = new Stone(Color.White);
}
public static StoneFactory Instance()
{
if (StoneFactory_stonefactory == null)
{
StoneFactory_stonefactory=new StoneFactory();
}
return StoneFactory_stonefactory;
}
#endregion
//根据部署获得棋子,奇数为黑子,偶数为白子
public Stone GetStone(int n)
{
int m;
int c = Math.DivRem(n, 2, out m);
if (m == 1)
{
return blackstone;
}
else
{
return whitestone;
}
}
}
}
Form1
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using I_go.AppCode;
namespace I_go
{
public partial class Form1 : Form
{
private Board b;
public Form1()
{
InitializeComponent();
DrawChessBoard();
}
private void newToolStripMenuItem_Click(object sender, EventArgs e)
{
DrawChessBoard();
}
//初始化
private void DrawChessBoard()
{
if (this.panel1.Width > this.panel1.Height)
{
this.panel1.Width = this.panel1.Height;
}
else
{
this.panel1.Height = this.panel1.Width;
}
Graphics g = this.panel1.CreateGraphics();
b = new Board(g);
b.Width = this.panel1.Width;
b.Draw();
b.DrawStones();
}
//刷新
private void ReDrawChessBoard()
{
Graphics g = this.panel1.CreateGraphics();
b.Width = this.panel1.Width;
b.Draw();
b.DrawStones();
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
ReDrawChessBoard();
}
private void panel1_MouseUp(object sender, MouseEventArgs e)
{
b.AddStone(e);
}
private void refrushToolStripMenuItem_Click(object sender, EventArgs e)
{
ReDrawChessBoard();
}
}
}
以上仅是一个讲解享元模式的示例代码。如果要成为一个围棋游戏,还有很多细节需要考虑。
相关模式
享元模式适用于大量的细粒度对象的场合,很多模式中的对象可以作为享元共享。
1.组合模式:组合中的组件可以作为享元使用。
2.策略模式:策略对象中可以作为享元供多个对象使用。
3.解释器模式:解释器定义的终结符经常作为享元来避免生成大量的终结符实例。
4.状态模式:若干具有相同行为的对象可共享不同状态下的行为。
享元模式的实现关键是注册工厂,由工厂创建共享对象并注册,为客户返回被共享的对象。