一、概述
在软件开发有时需要创建大量细粒度的对象(比如:文档处理系统就可能需要创建成千上万的字符对象)。但如果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)注意实现享元工厂时,只有具体的享元对象不存在于内存时才去新建否则失去享元的意义。