在我们面向对象编程时,我们会经常将很多对象封装在一起,成为对象A,如果A中的某个对象所占用的内存比较大,同时我们需要创建很多对象A,我们需要一种设计模式来解决这个问题。打个比方,假设有一个网络游戏,同时有很多用户在线,那么我们就要创建很多网游用户对象,但是每个用户对象都包含了衣服对象,但是这个衣服对象的类别就3中,我们肯定不希望为每个用户来创建一个衣服对象,那么我们就将衣服对象放入一个共享池里,那么所有的用户都使用这个共享池里面的衣服,我们就通过这种方式来节省了衣服这快的内存。
- class User
- {
- /// <summary>
- /// 用户名
- /// </summary>
- string Name;
- /// <summary>
- /// 用户武器
- /// </summary>
- string Weapon;
- /// <summary>
- /// 用户性别
- /// </summary>
- string Gender;
- /// <summary>
- /// 用户衣服
- /// </summary>
- public UserClother Clother;
- }
- class UserClother
- {
- /// <summary>
- /// 衣服名,确定衣服的唯一性
- /// </summary>
- string ClotherName;
- /// <summary>
- /// 衣服的其他属性
- /// </summary>
- string OtherProfile;
- public UserClother(string ClotherName)
- {
- this.ClotherName = ClotherName;
- }
- }
- static class UserClotherFactory
- {
- private static Hashtable clotherList = new Hashtable();
- public static UserClother GetUserClose(string ClotherName)
- {
- //从共享池中得到衣服对象
- UserClother clother = clotherList[ClotherName] as UserClother;
- //如果共享池里面不存在这个对象那么就创建这个对象,并加入到共享池中
- if (clother == null)
- {
- clother = new UserClother(ClotherName);
- clotherList.Add(ClotherName, clother);
- }
- return clother;
- }
- }
- class Program
- {
- static void Main(string[] args)
- {
- Console.WriteLine(GC.GetTotalMemory(false));
- //假设有10万个用户在线
- ArrayList userList = new ArrayList(100000);
- //假设衣服有三种
- string clother1 = "Clother1";
- string clother2 = "Clother2";
- string clother3 = "Clother3";
- User objUser = null;
- for (int i = 0; i < 100000; i++)
- {
- if (i % 3 == 0)
- {
- objUser = new User();
- objUser.Clother = UserClotherFactory.GetUserClose(clother1);
- //objUser.Clother = new UserClother(clother1);
- }
- else if (i % 3 == 1)
- {
- objUser = new User();
- objUser.Clother = UserClotherFactory.GetUserClose(clother2);
- //objUser.Clother = new UserClother(clother2);
- }
- else if (i % 3 == 2)
- {
- objUser = new User();
- objUser.Clother = UserClotherFactory.GetUserClose(clother3);
- //objUser.Clother = new UserClother(clother3);
- }
- userList.Add(objUser);
- }
- Console.WriteLine(GC.GetTotalMemory(false));
- Console.ReadLine();
- }
- }
- Main函数里注销的代码是不使用享元模式的代码。我们可以看到,在有10万用户使用的时候,我们衣服的对象始终只有3个,而不是30万个,这样我们就大大的节省了内存。
- 现在我们可以知道了享元模式使用在对象中有大量耗费了大量内存的细粒度对象,并且对外界来说这些对没有任何差别的(或者说经过改造后可以是没有差别的)。如果衣服有很多种,各式各样,如有10万种组合,那么使用享元模式基本上没有什么意义(节省不了多少内存),因为维护共享池的内存开销与直接在用户对象里指定新的衣服对象使用的内存差别不大。
- 实现要点
享元工厂维护一张享元实例表。
享元不可共享的状态需要在外部维护。
按照需求可以对享元角色进行抽象。
注意事项
享元模式通常针对细粒度的对象,如果这些对象比较拥有非常多的独立状态(不可共享的状态),或者对象并不是细粒度的,那么就不适合运用享元模式。维持大量的外蕴状态不但会使逻辑复杂而且并不能节约资源。
享元工厂中维护了享元实例的列表,同样也需要占用资源,如果享元占用的资源比较小或者享元的实例不是非常多的话(和列表元素数量差不多),那么就不适合使用享元,关键还是在于权衡得失