设计模式之享元模式
名词解释:
享元模式(Flyweight):运用共享技术有效地支持大量细粒度的对象。
必要元素:
1.一个享元工厂,用来创建并管理Flyweight对象。它主要是用来确保合理地共享Flyweight,当用户请求一个Flyweight时,FlyweightFactory对象提供一个已创建的实例或者创建一个(如果不存在);
2.所有具体享元类的父类或接口,通过这个接口,Flyweight可以接受并作用于外部状态;
3.继承Flyweight父类或者实现Flyweight接口,并为内部状态增加存储空间;
4.指那些不需要共享的Flyweight子类,因为Flyweight接口共享成为可能,但是并不强制共享(即那些不能共享的部分)。
上例子:
Flyweight享元抽象类:
abstract class Flyweight { public abstract void Operation(int extrinsicstate); }
Flyweight具体共享类:
class ConcreteFlyweight:Flyweight { public override void Operation(int extrinsicstate) { Console.WriteLine("具体Flyweight:"+extrinsicstate); } } class UnsharedConcreteFlyweight:Flyweight { public override void Operation(int extrinsicstate) { Console.WriteLine("不共享的具体Flyweight:"+extrinsicstate); } }
享元工厂:
class FlyweightFactory { private Dictionary<string, Flyweight> flyweights = new Dictionary<string, Flyweight>(); 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]; } }
调用:
//外部状态 int extrinsicstate = 22; FlyweightFactory factory = new FlyweightFactory(); Flyweight fx = factory.GetFlyweight("X"); fx.Operation(--extrinsicstate); Flyweight fy = factory.GetFlyweight("Y"); fx.Operation(--extrinsicstate); Flyweight fz = factory.GetFlyweight("Z"); fz.Operation(--extrinsicstate); Flyweight uf = new UnsharedConcreteFlyweight(); uf.Operation(--extrinsicstate); Console.Read();
通过享元类父类和子类(共享和非共享部分)以及享元类工厂,构成了享元模式,这样在使用的时候就不会创建多次对象,有效的利用资源。
例子2(假设有一个网站类,而网站有很多种类型):
抽象类:
/// <summary> /// 网站抽象类 /// </summary> abstract class WebSite { public abstract void Use(); }
具体网站类:
/// <summary> /// 具体网站类 /// </summary> class ConcreteWebSite:WebSite { private string name = ""; public ConcreteWebSite(string name) { this.name = name; } public override void Use() { Console.WriteLine("网站分类:"+name); } }
网站工厂类:
/// <summary> /// 网站工厂 /// </summary> class WebSiteFactory { private Dictionary<string, WebSite> flyweights = new Dictionary<string, WebSite>(); /// <summary> /// 获得网站分类 /// </summary> /// <param name="key"></param> /// <returns></returns> public WebSite GetWebSite(string key) { if (!flyweights.ContainsKey(key)) { flyweights.Add(key, new ConcreteWebSite(key)); } return flyweights[key]; } public int GetWebSiteCount() { return flyweights.Count; } }
使用:
WebSiteFactory factory = new WebSiteFactory(); WebSite fx = factory.GetWebSite("产品展示"); fx.Use(); WebSite fy = factory.GetWebSite("产品展示"); fy.Use(); WebSite fz = factory.GetWebSite("产品展示"); fz.Use(); WebSite fl = factory.GetWebSite("博客"); fl.Use(); WebSite fm = factory.GetWebSite("博客"); fm.Use(); WebSite fn = factory.GetWebSite("博客"); fn.Use(); Console.WriteLine("共有网站{0}个",factory.GetWebSiteCount()); Console.Read();
这样设计基本实现了享元模式的共享对象的目的,也就是说不管创建几个网站,只要是某一类型(博客或者其他),都是完全相同的,但是问题又来了,不同公司的网站应该有不同的账号,所以我们只是体现了共享的部分,还有非共享部分等待我们实现。
实例2(集成非共享代码):
外部状态类(User类):
/// <summary> /// 用户类(外部状态) /// </summary> class User { private string name; public string Name { get { return name; } } public User(string name) { this.name = name; } }
抽象网站类:
/// <summary> /// 网站抽象类 /// </summary> abstract class WebSite { public abstract void Use(User user); }
方法增加了参数,参数为外部状态对象User。
具体网站类:
/// <summary> /// 具体网站类 /// </summary> class ConcreteWebSite:WebSite { private string name = ""; public ConcreteWebSite(string name) { this.name = name; } public override void Use(User user) { Console.WriteLine("网站分类:"+name+" 用户:"+user.Name); } }
变化也是User函数添加User对象参数。
工厂:无变化。
使用:
WebSiteFactory factory = new WebSiteFactory(); WebSite fx = factory.GetWebSite("产品展示"); fx.Use(new User("Colors")); WebSite fy = factory.GetWebSite("产品展示"); fy.Use(new User("Blue")); WebSite fz = factory.GetWebSite("产品展示"); fz.Use(new User("Listen")); WebSite fl = factory.GetWebSite("博客"); fl.Use(new User("Colors.Blue")); WebSite fm = factory.GetWebSite("博客"); fm.Use(new User("Listen.Fly")); WebSite fn = factory.GetWebSite("博客"); fn.Use(new User("李莫愁")); Console.WriteLine("共有网站{0}个",factory.GetWebSiteCount()); Console.Read();
这样虽然是同一个类型的网站,但是有了User这个外部状态对象,所以就可以把内部状态与外部状态很好的结合。
总结:
如果一个应用程序使用了大量的对象,而大量的对象造成了很大的存储开销时就应该考虑使用享元模式;对象的大多数状态可以外部状态,如果删除对象的外部状态,那么可以用相对较少的共享对象取代很多组对象,此时可以考虑使用享元对象。在实际应用中,使用了享元模式实例总数就大大减少了。