一、概述
在软件开发有时需要创建大量细粒度的对象(比如:文档处理系统就可能需要创建成千上万的字符对象)。但如果new一个新的字符对象就会分配内存空间,那么在系统运行时就会耗费大量的内存资源。如何在保留面向对象操作方式优点的同时避免创建大量的对象呢?这就到了享元模式发挥作用的时候了。
二、享元模式
享元模式运用共享技术有效地支持大量细粒度的对象。例如可以对文档处理系统创建共享池(内存),在共享池中建立字母和代码的对应关系,这样就可以用共享池中的对象解决需要创建大量对象的问题。其结构图如下:
Flyweight定义了享元接口,外部对象通过这个接口来访问具体的享元对象。
ConcreteFlyweight实现Flyweight接口,定义了具体的享元对象,并保存享元对象的内部状态。该享元对象是可共享的。
UnsharedConcreteFlyweight实现Flyweight接口,定义了不用于共享的享元对象。
FlyweightFactory创建并管理享元对象。
Client保存对享元接口的引用,通过该引用有效的使用具体的享元对象。
三、示例(由于我们做成动态创建对象形式所有不用抽象类)
我们实现一个简单的文本编辑系统。
1.创建字型类:
public class Character
{
public Color Color { get; set; }
private char alphabet;
public Character(char c)
{
this.alphabet = c;
this.Color = Color.Black;
}
public Character()
{
}
public char Alphabet
{
get { return alphabet; }
private set { alphabet = value; }
}
}
2. 创建字型工厂类:
public class CharacterFactory
{
private static Dictionary<char, Character> _character=new Dictionary<char,Character>();
private static CharacterFactory glyphsfactory = new CharacterFactory();
public CharacterFactory()
{
}
public static CharacterFactory getInstance()
{
return glyphsfactory;
}
public Character GetCharacter(char c)
{
if (_character == null || !_character.ContainsKey(c))
{
Character character = new Character(c);
_character.Add(c, character);
}
return _character[c];
}
}
3.创建坐标类:
public class Position
{
private int x;
public int X
{
get { return x; }
private set { x = value; }
}
private int y;
public int Y
{
get { return y; }
private set { y = value; }
}
public Position(int x, int y)
{
this.x = x;
this.y = y;
}
}
4.创建坐标工厂类:
public class PositionFactory
{
private static PositionFactory positionFactory = new PositionFactory();
private Hashtable positionTable = new Hashtable();
static PositionFactory()
{
}
public static PositionFactory getInstance()
{
return positionFactory;
}
public Position GetPosition(int X, int Y)
{
string key = X.ToString() + "|" + Y.ToString();
if (positionTable == null || !positionTable.ContainsKey(key))
{
Position position = new Position(X, Y);
positionTable.Add(key, position);
}
return (Position)positionTable[key];
}
}
5. 看一下具体调用(测试代码略):
public class Program
{
static void Main(string[] args)
{
string Data = "Do not dwell in the past, do not dream of the future, concentrate the mind on the present moment.";
char[] characters = new char[120];
characters = Data.ToCharArray();
CharacterFactory characterFactory = CharacterFactory.getInstance();
PositionFactory positionFactory = PositionFactory.getInstance();
Character character;
Position position;
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 40; j++)
{
if ((j + (i * 40)) <= 98)
{
character = characterFactory.GetCharacter(characters[j + (i * 40)]);
position = positionFactory.GetPosition(i, j);
setColor(character, i, j);
Console.WriteLine("{0}[{1}]({2},{3})", character.Alphabet, character.Color.Name.ToString(), position.X, position.Y);
}
}
}
Console.ReadKey();
}
private static void setColor(Character character, int i, int j)
{
if (j + (i * 40) >= 7 && j + (i * 40) <= 11)
{
character.Color = Color.Red;
}
else if (j + (i * 40) >= 20 && j + (i * 40) <= 23)
{
character.Color = Color.Orange;
}
else if (j + (i * 40) >= 33 && j + (i * 40) <= 37)
{
character.Color = Color.Yellow;
}
else if (j + (i * 40) >= 47 && j + (i * 40) <= 52)
{
character.Color = Color.Green;
}
else if (j + (i * 40) >= 55 && j + (i * 40) <= 65)
{
character.Color = Color.Blue;
}
else if (j + (i * 40) >= 71 && j + (i * 40) <= 74)
{
character.Color = Color.Indigo;
}
else if (j + (i * 40) >= 92 && j + (i * 40) <= 97)
{
character.Color = Color.Violet;
}
else
{
character.Color = Color.Black;
}
}
}
6.问题总结:
1)Ilist插入引用对象问题:在测试代码中将字型对象插入到Ilist<character>中出现字型对象color属性测试不通过,原因是引用类型是以地址形式插入Ilist<character>中,当相同字符的字型对象先后插入时先插入的color属性会被覆盖。
2)各位匠友可以试着把工厂类抽象,做到充分解耦。
3)注意实现享元工厂时,只有具体的享元对象不存在于内存时才去新建否则失去享元的意义。