设计模式:享元模式
一、什么是享元模式?
享元模式(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