小话设计模式(十一)享元模式
2016-10-01 17:24
519 查看
享元(Fly Weight直译作蝇量)模式运用共享技术有效的支持大量细颗粒的对象。
一般使用享元模式的时候要满足下列所有情况:1、 一个应用程序使用了大量的对象。
2、 完全由于使用大量的对象,造成了很大的存储开销。
3、 对象的大多数状态都可以变外外部状态。
4、 如果删除对象的外部状态,那么可以用相对较少的共享对象取代很多组对象。
5、 应用程序不依赖与对象标识。
举个例子
一个RPG游戏,怪兽或者英雄都可以改变等级,改变等级之后,二者的攻击力都会发生变化。我们将等级数据(每一级对应的攻击力)存放在本地数据文件里。当我们需要创建一个对象的时候,就要获取这些等级数据。
游戏里的等级数据定义如下:
public abstract class LevelData { protected virtual void LoadFromFile (string filePath) { //TODO: } public abstract int GetAttack (ClientData data); }
这些数据都是从本地数据文件中读取出来的,这些数据对于每个怪兽都是一样的,所以是共享数据。而且,这些数据是不会变的,除非修改数据文件。
这里提供了一个GetAttack的公开方法,用于获取(怪兽、英雄)攻击力
怪兽等级数据:
public class MonsterLevelData : LevelData { public int ID { get; private set;} public MonsterLevelData(int monID) { ID = monID; //For different heroes, load different files. LoadFromFile (monID.ToString()); } private List<int> _levelAttackList = new List<int> ();//Loaded in the method LoadFromFile protected override void LoadFromFile(string filePath) { base.LoadFromFile (filePath); //TEST for (int i = 1; i <= 10; i++) { _levelAttackList.Add (i * 10); } } public override int GetAttack (ClientData data) { int lv = data.Level; lv = System.Math.Min (System.Math.Max (lv, 1), 10); int attack = _levelAttackList [lv - 1]; return attack + data.AttackPlus; } }
接着,我们定义了怪兽的类,包含了怪兽的等级和攻击力加成。在创建怪兽对象的时候,我们就可以根据这两个值的不同,获取不同的攻击力。因为是可变的,所以被定义为外部数据:
public class ClientData { public int Level {get;set;} public int AttackPlus { get; set;} }
那么如何共享对象呢?这里需要建立一个享元工厂:
public static class LevelDataFactory { public static Dictionary<int, LevelData> _monsterLevelData = new Dictionary<int, LevelData>(); public static LevelData GetMonsterLevelData(int monID) { if (!_monsterLevelData.ContainsKey (monID)) { _monsterLevelData [monID] = new MonsterLevelData (monID); } return _monsterLevelData [monID]; } }
因为我们希望相同的怪兽应该拥有相同的等级数据,所以我们使用这样一个享元工厂来创建等级数据,对于相同的怪兽,它们共享同一个等级数据。
使用:
LevelData levelData = LevelDataFactory.GetMonsterLevelData (1); ClientData monster = new ClientData (); monster.Level = 3; monster.AttackPlus = 5; Console.WriteLine (levelData.GetAttack(monster));
这里有一个问题,因为将等级、攻击加成等信息当做外部状态,放在了ClientData里,我们修改这些值得时候,是修改ClientData的实例,但是获取最终结果的时候,却调用的是levelData的方法GetAttack。这样不是很方便,并且容易造成困扰。所以我们可以考虑修改享元模式,把GetAttack的方法写在用户数据里。
为LevelData增加一个抽象方法:
public abstract int GetLevelAttack(int lv);
MonsterLevelData里添加实现:
public override int GetLevelAttack(int lv) { lv = System.Math.Min (System.Math.Max (lv, 1), 10); return _levelAttackList[lv - 1]; }
创建继承自ClientData的Monster类:
public class Monster : ClientData { LevelData _levelData; public Monster(int monID) { _levelData = LevelDataFactory.GetMonsterLevelData (monID); } public int GetAttack() { return _levelData.GetLevelAttack (Level) + AttackPlus; } }
使用:
Monster mon = new Monster (2); mon.Level = 3; mon.AttackPlus = 4; Console.WriteLine (mon.GetAttack ());这样就将内部状态里的方法移动到了外部状态里。调用很方便,也节省了内存。
享元模式的优点在于:减少了运行时对象实例的个数,节省了内存,并且可以集中管理对象的多个状态。
缺点也很明显:个体之间无法相对独立,拥有不同的行为。
相关文章推荐
- Java设计模式之十一(享元模式)
- 设计模式之(十一)--享元模式(flyweight)
- java设计模式(十一)——享元模式(Flyweight)
- 设计模式(十一):享元模式
- 设计模式学习(十一) 享元模式
- 设计模式之(十一)享元模式Flyweight
- java设计模式(十一)--享元模式
- 设计模式笔记之十一 (享元模式)
- C#设计模式之十一享元模式(Flyweight Pattern)【结构型】
- C#设计模式之十一享元模式(Flyweight Pattern)【结构型】
- 设计模式之(十一)享元模式Flyweight
- 设计模式之享元模式(卷十一)
- 乐在其中设计模式(C#) - 享元模式(Flyweight Pattern)
- 经验技巧分享--ASP.NET和Ajax应用一个超级实用的设计模式---享元模式
- 设计模式循序渐进(4)享元模式 Flyweight
- 设计模式中结构型模式(六)享元模式(Facade)
- 乱砍设计模式之十一 -- 其他
- 设计模式学习(十一)行为型模式 简单介绍
- ASP.NET和Ajax应用一个超级实用的设计模式---享元模式
- 设计模式学习(九)外观模式-享元模式-代理模式