设计模式深入学习--Flyweight 享元模式(结构型模式)
2017-01-16 10:22
603 查看
这次和大家一起学习一下 享元设计模式。我们在开发游戏的时候经常可能会遇到使用大量细粒度对象,比如三消游戏的 这些一个个小方块。又或者一个大型模拟游戏里面的Ai人物,或者围棋里面的黑白子,等等。。。
我们一般的优化方法就是使用对象池,将使用的拿出来,不使用了又放回去,这样的方法虽然好,但还是没有根本解决对象被大量new出来的事实,导致内存变得非常大,而Flyweight
享元模式就是通过共享技术来有效地支持大量细粒度的对象,从而解决这样的问题。我们先来看一个Flyweight
享元模式的示例代码:
就把这些不共享的数据存放到该类下。最后就是来构造一个工厂类flyweightFactory。 工厂类初始化一个Hashtable 字典,用来存放共有类数据。还有一个返回类型的value的方法。ok,模板有了,我们就在初始化的时候声明一下参数,运行起来看看效果:
现在我们运行一下看看,可以看到,已经就打印出来了3个具体的ConcreteFlyweight类,而其实他们都是一个实例。原理我们弄懂了,我们再来自己做个小案例玩玩,展示下享元设计模式的强大用法。
我们模仿制作一个敌人类Eneny,准确来说应该是一个多种类型兵种的管理器,
首先是一个敌人抽象类,里面调用到一个User类,user类存放该人物名字,这个就代表我们在示例里面的不共享类。 然后是一个具体的敌人类ConcreteEneny,重写了父类User方法。敌人工厂类EnenyFactory
,存放敌人类的Hashtable字典,还有两个方法,一个获得敌人的分类。 一个返回分类的实例化总数。最后,实例化看看效果.
代码可以看到,我们声明了人族和兽族两个分类,然后分别传入了矮人火枪手,步兵,农民
和苦工,巨魔猎头者,牛头人 ,表面上看起来我们构造了6个不同的敌人,但是输出我们可以看到分类总数其实只有2个,他们共享了一套相同的模板,只是名字不同样,是由我们外部传入的。
现在可以看出Flyweight 享元模式的好处了吧,当我们需要大量复用一个细粒度高的对象时候,我们可以采用享元设计模式,可以给我们的CPU,内存减少了极大的开销,只要能共享的数据就都采用一个模板复用,不能共享的数据就采用外部传入的方式来实现。我们再来复习下Flyweight
享元模式的几个要点:
Flyweight 享元模式主要解决面向对象的代价问题,一般不触及面向对象的抽象性问题。
Flyweight 享元模式采用对象共享的做法来降低系统中对象的个数,从而降低细粒度对象给系统带来的内存压力,在具体实现方面,要注意对象状态的处理。
对象的数量太大从而导致对象内存开销加大,什么样的数量才算大?这需要我们仔细地根据具体应用情况进行评估,而不能意淫的拍脑袋决定。
我们一般的优化方法就是使用对象池,将使用的拿出来,不使用了又放回去,这样的方法虽然好,但还是没有根本解决对象被大量new出来的事实,导致内存变得非常大,而Flyweight
享元模式就是通过共享技术来有效地支持大量细粒度的对象,从而解决这样的问题。我们先来看一个Flyweight
享元模式的示例代码:
/// <summary> /// flyweight类 拥有享元类的超类或接口,通过这个接口,flyweight可以接受并作用于外部状态 /// </summary> abstract class flyweight { public abstract void Operation(int extrinsicstate); } /// <summary> /// ConcreteFlyweight是继承flyweight超类货实现flyweight接口,并为内部状态增加存储空间 /// </summary> class ConcreteFlyweight : flyweight { public override void Operation(int extrinsicstate) { Console.WriteLine("具体flyweight:" + extrinsicstate); } } /// <summary> /// UnsharedConcreteFlyweight是指那些不需要共享的flyweight子类 因为flyweight接口共享成为可能,但它并不强制共享 /// </summary> class UnsharedConcreteFlyweight : flyweight { public override void Operation(int extrinsicstate) { Console.WriteLine("不共享的具体flyweight:" + extrinsicstate); } } /// <summary> /// flyweightFactory,是一个享元工厂,用来创建并管理flyweight对象,它主要用来确保合理地共享flyweight, /// 当用户请求一个flyweight时,flyweightFactory对象提供一个已创建的实例或者创建一个 /// </summary> class flyweightFactory { private Hashtable flyweights = new Hashtable(); public flyweightFactory() { flyweights.Add("X", new ConcreteFlyweight()); flyweights.Add("Y", new ConcreteFlyweight()); flyweights.Add("Z", new ConcreteFlyweight()); } public flyweight GetFlyweight(string key) { return ((flyweight)flyweights[key]); } }从示例可以看出,我们先构造一个虚类flyweight ,然后和一个具体实现的子类ConcreteFlyweight,这个子类就是共享的数据类,比如要做一把枪,枪的类型不一样,外观不一样,但都有一个共同点,就是有一个能开枪的方法,或者存储其他共有的数据。而就可以用ConcreteFlyweight类来存放。接下来再构造一个子类UnsharedConcreteFlyweight,而刚才说的外观,类型不一样
就把这些不共享的数据存放到该类下。最后就是来构造一个工厂类flyweightFactory。 工厂类初始化一个Hashtable 字典,用来存放共有类数据。还有一个返回类型的value的方法。ok,模板有了,我们就在初始化的时候声明一下参数,运行起来看看效果:
int extrinsicstate = 22; flyweightFactory ff = new flyweightFactory(); flyweight fx = ff.GetFlyweight("X"); fx.Operation(--extrinsicstate); flyweight fy = ff.GetFlyweight("Y"); fy.Operation(--extrinsicstate); flyweight fz = ff.GetFlyweight("Z"); fz.Operation(--extrinsicstate); UnsharedConcreteFlyweight uf = new UnsharedConcreteFlyweight(); uf.Operation(--extrinsicstate); Console.ReadKey();
现在我们运行一下看看,可以看到,已经就打印出来了3个具体的ConcreteFlyweight类,而其实他们都是一个实例。原理我们弄懂了,我们再来自己做个小案例玩玩,展示下享元设计模式的强大用法。
/// <summary> /// 敌人抽象类 /// </summary> abstract class Eneny { public abstract void Use(User user); } /// <summary> /// 具体敌人类 /// </summary> class ConcreteEneny : Eneny { private string name = ""; public override void Use(User user) { Console.WriteLine("敌人分类:" + name+ '\n'+"名字:"+user.Name); } public ConcreteEneny(string name) { this.name = name; } } /// <summary> /// 敌人工厂类 /// </summary> class EnenyFactory { private Hashtable flyweights=new Hashtable(); //获得敌人分类 public Eneny GetEnenyCategory(string key) { if (!flyweights.ContainsKey(key)) { flyweights.Add(key,new ConcreteEneny(key)); } return ((Eneny) flyweights[key]); } public int GetEnenyCount() { return flyweights.Count; } } /// <summary> /// 用户类 /// </summary> public class User { private string name; public User(string name) { this.name = name; } public string Name { get { return name; } } }
我们模仿制作一个敌人类Eneny,准确来说应该是一个多种类型兵种的管理器,
首先是一个敌人抽象类,里面调用到一个User类,user类存放该人物名字,这个就代表我们在示例里面的不共享类。 然后是一个具体的敌人类ConcreteEneny,重写了父类User方法。敌人工厂类EnenyFactory
,存放敌人类的Hashtable字典,还有两个方法,一个获得敌人的分类。 一个返回分类的实例化总数。最后,实例化看看效果.
EnenyFactory wsf = new EnenyFactory(); //实例化 Eneny fx = wsf.GetEnenyCategory("人族"); fx.Use(new User("矮人火枪手")); //共享上面生成的对象,不再实例化 Eneny fy = wsf.GetEnenyCategory("人族"); fy.Use(new User("步兵")); //共享上面生成的对象,不再实例化 Eneny fz = wsf.GetEnenyCategory("人族"); fz.Use(new User("农民")); //实例化 Eneny fl = wsf.GetEnenyCategory("兽族"); fl.Use(new User("苦工")); //共享上面生成的对象,不再实例化 Eneny fm = wsf.GetEnenyCategory("兽族"); fm.Use(new User("巨魔猎头者")); //共享上面生成的对象,不再实例化 Eneny fn = wsf.GetEnenyCategory("兽族"); fn.Use(new User("牛头人")); Console.WriteLine("敌人分类总数:" + wsf.GetEnenyCount()); Console.ReadKey();
代码可以看到,我们声明了人族和兽族两个分类,然后分别传入了矮人火枪手,步兵,农民
和苦工,巨魔猎头者,牛头人 ,表面上看起来我们构造了6个不同的敌人,但是输出我们可以看到分类总数其实只有2个,他们共享了一套相同的模板,只是名字不同样,是由我们外部传入的。
现在可以看出Flyweight 享元模式的好处了吧,当我们需要大量复用一个细粒度高的对象时候,我们可以采用享元设计模式,可以给我们的CPU,内存减少了极大的开销,只要能共享的数据就都采用一个模板复用,不能共享的数据就采用外部传入的方式来实现。我们再来复习下Flyweight
享元模式的几个要点:
Flyweight 享元模式主要解决面向对象的代价问题,一般不触及面向对象的抽象性问题。
Flyweight 享元模式采用对象共享的做法来降低系统中对象的个数,从而降低细粒度对象给系统带来的内存压力,在具体实现方面,要注意对象状态的处理。
对象的数量太大从而导致对象内存开销加大,什么样的数量才算大?这需要我们仔细地根据具体应用情况进行评估,而不能意淫的拍脑袋决定。
相关文章推荐
- 设计模式学习之享元模式(Flyweight,结构型模式)(20)
- 【设计模式学习笔记十二】【结构型模式】【享元模式(FlyWeight)】
- 设计模式--享元模式FlyWeight(结构型)
- 设计模式学习笔记(十二)——Flyweight享元模式
- [设计模式-结构型]享元模式(Flyweight )
- 设计模式(十)享元模式Flyweight(结构型)
- [导入]C#面向对象设计模式纵横谈(12):Flyweight 享元模式(结构型模式).zip(7.86 MB)
- 设计模式学习笔记--Flyweight享元模式
- 设计模式学习笔记(二十二)—FlyWeight享元模式
- 步步为营 .NET 设计模式学习笔记 十七、Flyweight(享元模式)
- 设计模式(11)-结构型-享元模式(Flyweight)
- 设计模式学习8 -- Flyweight(享元模式)
- [设计模式-结构型]享元模式(Flyweight )
- PHP设计模式:结构型之享元模式(Flyweight)
- C#面向对象设计模式纵横谈 学习笔记12 FlyWeight享元模式(结构型模式)
- 设计模式(十)享元模式Flyweight(结构型)
- 从FLC中学习的设计模式系列-结构型模式(3)-享元模式
- 设计模式学习笔记(十二)——Flyweight享元模式
- 步步为营 .NET 设计模式学习笔记 十七、Flyweight(享元模式)
- 面向对象设计模式之Flyweight享元模式(结构型)