第二十二章 享元模式(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.状态模式:若干具有相同行为的对象可共享不同状态下的行为。
享元模式的实现关键是注册工厂,由工厂创建共享对象并注册,为客户返回被共享的对象。