设计模式:享元模式 一、什么是享元模式? 享元模式(Flyweight) ,运用共享技术有效地支持大量细粒度的对象。UML结构图如下:
Flyweight是抽象享元角色。它是产品的抽象类,同时定义出对象的外部状态和内部状态的接口或实现;ConcreteFlyweight是具体享元角色,是具体的产品类,实现抽象角色定义的业务; UnsharedConcreteFlyweight是不可共享的享元角色,一般不会出现在享元工厂中; FlyweightFactory是享元工厂,它用于构造一个池容器,同时提供从池中获得对象的方法。
二、内部状态与外部状态的区分 享元享元,共享细粒度的单元。那么什么是细粒度的单元呢?如果用乐高积木作比喻,那么一个积木人可以称为一个完整的对象。如果我们把积木人拆开,可以进一步得到头、身躯,腿三个部分。而这些部分,相比于完整的积木人而言,它们三个就是细粒度的单元。
那么为什么要把一个完整的对象区分内外部呢?这岂不是增加了代码的复杂度?好,我们暂时搁置,接下来思考这样一个问题,如果我们要创造 10 个积木人,用程序怎么表示呢?
我们先创建一个积木人的类,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 public class LegoMan { public string Head; public string Torso; public string Leg; public LegoMan (string head, string torso, string leg ) { this .Head = head; this .Torso = torso; this .Leg = leg; } }
接着我们开始创造积木人,先采用直接 new 的方式:
1 2 3 4 5 6 7 8 9 public class Example { public static void main (String[] args ) { LegoMan man1 = new LegoMan("head1" , "torso1" , "leg1" ); LegoMan man2 = new LegoMan("head2" , "torso2" , "leg2" ); ... LegoMan man10 = new LegoMan("head10" , "torso10" , "leg10" ); } }
现在我们得到了 10 个积木人,接下来我们对积木人作出一些限制,我们现在需要 10 个士兵积木人,由于士兵的制服统一,那么这一百个积木人的下半身是完全一样的,也就是说除了 Head,积木人的 Torso 和 Leg 都是一样的。现在我们继续创建十个士兵积木人。好吧,和上面的例子一样,只是传入的后两个参数均一致。
1 LegoMan manN = new LegoMan("headN" , "torsoStandard" , "legStandard" )
接下来,我们思考这样一个问题,如果需要 1000 个士兵积木人呢?如果采用一般的方式,需要创建 1000 个实例对象,但是这 1000 个对象都有着共同的部分,就是它们的 Torso 和 Leg。那我们可不可以把共同的部分抽取出来呢?当然可以,现在我们把 Torso 和 Leg 整合为一个 Body 类,如下:
1 2 3 4 5 6 7 8 9 10 11 public class Body { public string Torso; public string Leg; public Body (string torso, string leg ) { this .Torso = torso; this .Leg = leg; } }
既然 Body 被提取出来了,那么 LegoMan 这个类也要被重写了,如下:
1 2 3 4 5 6 7 8 9 10 11 public class LegoMan { public string Head; public Body BodyIntrinsic; public LegoMan (string head, Body body ) { this .Head = head; this .BodyIntrinsic = body; } }
现在我们再来创建 1000 个积木人士兵的话,应该是这样:
1 2 3 4 5 6 7 8 9 10 11 12 public class Example { public static void main (String[] args ) { Body bodyStandard = new Body("torsoStandard" , "legStandard" ); LegoMan man1 = new LegoMan("head1" , bodyStandard); LegoMan man2 = new LegoMan("head2" , bodyStandard); ... LegoMan man1000 = new LegoMan("head1000" , bodyStandard); } }
发现了吗?现在虽然也是1000个 LegoMan 的实例,但是却只有一个 Body,也就是说,1000个积木人士兵 的 Head,共享了一个 Body。听起来很疯狂,九头蛇也才九头,一千个头的怪物得多可怕!?哈哈,虽然积木人的玩具不可能这么拼,但是程序里,这种共享机制是可行的。Body 就是享元模式中的内部状态,一个重复度很高的细粒度单元。而 Head 则对应外部状态,会随着需求发生变化。这么分离的好处也很明显,就是大大减少了总数据量。如果不分离内外部,创建 1000 个积木人士兵的成本就是1000个 Head 和1000个 Body,而采用分离策略后,就只需要1000个 Head 外加1个 Body了。当随着创建对象数量级的增大,这种策略带来的好处会越来越明显。
三、完整的享元模式 理解了分离内外部的原因后,下面简单实现一下享元模式
1.Flyweight抽象类 通过最上面的 UML 图可以看出,Flyweight 被分为两部分,ConcreteFlyweight(共享的内部)和UnsharedConcreteFlyweight(不可共享的外部)。所以 Flyweight 最好被做成接口,或者抽象类,这里用抽象类实现,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public abstract class Flyweight { public String intrinsic; public String extrinsic; public Flyweight (String extrinsic ) { this .extrinsic = extrinsic; } public abstract void Operate (int extrinsic ) ; }
2. ConcreteFlyweight类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class ConcreteFlyweight : Flyweight { public ConcreteFlyweight (String extrinsic ):base (extrinsic ) { Debug.Log("共享的 " + extrinsic); } public void Operate (string extrinsic ) { Debug.Log("处理共享数据 " + extrinsic); } }
3. UnsharedConcreteFlyweight类 1 2 3 4 5 6 7 8 9 10 11 12 13 public class UnsharedConcreteFlyweight : Flyweight { public UnsharedConcreteFlyweight (String extrinsic ):base (extrinsic ) { Debug.Log("非共享的 " + extrinsic); } public void Operate (int extrinsic ) { Debug.Log("处理非共享数据 " + extrinsic); } }
4. FlyweightFactory类 既然是处理大量数据,那免不了用一个对象池来进行管理,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public class FlyweightFactory { private static List<Flyweight> pool = new List<Flyweight>(); public static Flyweight GetFlyweight (String extrinsic ) { var flyweight = pool.Find(obj => obj.extrinsic == extrinsic); if (flyweight == null ) { flyweight = new ConcreteFlyweight(extrinsic); pool.Add(flyweight); Debug.Log("新创建 " + extrinsic); } else { Debug.Log("从池中取出 " + extrinsic); } return flyweight; } }
5.客户端的调用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class Client { public static void main (String[] args ) { Flyweight flyweight1 = FlyweightFactory.GetFlyweight("one" ); flyweight1.Operate("one" ); Flyweight flyweight2 = FlyweightFactory.GetFlyweight("two" ); flyweight2.Operate("two" ); Flyweight flyweight3 = FlyweightFactory.getFlyweight("one" ); flyweight3.Operate("one" ); Flyweight unsharedFlyweight = new UnsharedConcreteFlyweight("one" ); unsharedFlyweight.operate("one" ); } }
打印结果如下: 新创建 one 处理共享数据 one 新创建 two 处理共享数据 two 从池中取出 one 处理共享数据 one 非共享的 one 处理非共享数据 one
参考博客:https://www.cnblogs.com/adamjwh/p/9070107.html https://blog.csdn.net/justloveyou_/article/details/55045638