3.6 Flyweight(享元)
朋友们肯定都看过《西游记》,里面最帅的角色非孙悟空莫属,而孙悟空最厉害的技术实力就是72变,其实“72”只是个概述,实际上他可以想变什么就变什么。而这些变化里面,最为壮观的莫过于变小猴子,无论是央视83版《西游记》中的变小猴子大战天兵天将,还是《大话西游》中变小猴子大战牛虱,场面都蔚为壮观,而且《大话西游》表现的更为戏剧性,孙悟空还可以在一旁懒洋洋地观战。
我们不推荐每位读者都去学习如何拔汗毛吹气变小人儿,我们假设孙悟空变出小猴子的场景是发生在电脑当中,例如孙悟空拔10根汗毛吹口气变成10个小猴子站成一排威慑敌人,如何用程序设计的思维方式来实现这个过程呢?
一个很直接的办法是:实例化N个小猴子的实例对象,N也是汗毛根数,然后把这些实例对象塞进一个数组对象中去就可以了。这个方法实现起来的确是最直观简洁的,但是如果我们要考虑到性能问题呢?假设这个时候10个小猴子已经起不到威慑作用了,玉皇大帝派来50万天兵天将,那么孙悟空至少要弄出一只70万只猴子的野战部队来才有可能把对方镇住,难道要实例化70万个猴子的实力然后塞到一个常常的数组里去吗?我估计很少有计算机用户愿意花那么多准备时间来占用那么多内存容量的。
像这种情况我们就可以考虑使用享元模式了。享元,顾名思义就是“共享元素”的意思。我们假设在花果山只有两种猴子(像眼睛猴这种不擅长打架的外援暂不考虑引进):猕猴(Macaque)和金丝猴(Golden Monkey),那么我们就可以有两个类,MacaqueFlyweight和GoldenMonkeyFlyweight,那么我们只需要有2个猴子的对象实例就行了,它们的内部信息和外部信息完全分离开,比如它们有共同的站立方法,但是站立的位子则是作为外部信息传递给它们。我们把这两个猴子的对象放进一个共享数组中,每次取出一个猴子的实例然后告诉它站立的位置,就能够添加一个猴子了。这些猴子没有太多的内部信息,我们不需要记住它们每只猴子各自的长相、喜好、姓名和年龄等,我们对它们都是一视同仁,这样的场景就很符合享元模式的设计思路。
让我们来看看示例代码吧:
1: using System;
2: using System.Collections;
3:
4: namespace Autumoon.DesignPatterns.FlyweightDemo
5: {
6: public class FlyweightFactory
7: {
8: private ArrayList _monkeyTroop = null;
9:
10: public FlyweightFactory()
11: {
12: this._monkeyTroop = new ArrayList();
13: this._monkeyTroop.Add(new GoldenMondkyFlyweight());
14: this._monkeyTroop.Add(new MacaqueFlyweight());
15: }
16:
17: public Flyweight GetFlyweight(int key)
18: {
19: return ((Flyweight)this._monkeyTroop[key % 2]);
20: }
21: }
22:
23: public abstract class Flyweight
24: {
25: abstract public void Stand(int pointX, int pointY);
26: }
27:
28: public class GoldenMondkyFlyweight : Flyweight
29: {
30: override public void Stand(int pointX, int pointY)
31: {
32: Console.WriteLine("This golden monkey stands at [{0},{1}].", pointX, pointY);
33: }
34: }
35:
36: public class MacaqueFlyweight : Flyweight
37: {
38: override public void Stand(int pointX, int pointY)
39: {
40: Console.WriteLine("This macaque stands at [{0},{1}].", pointX, pointY);
41: }
42: }
43: }
遇到有小妖怪来踢馆,孙悟空随便拔10根汗毛变出10只小猴子站成一排,敌人也未必敢轻举妄动。
1: static void Main(string[] args)
2: {
3: #region Flyweight
4: FlyweightFactory flyweightFacotry = new FlyweightFactory();
5:
6: for (int i = 1; i <= 10; i++)
7: {
8: Flyweight flyweight = flyweightFacotry.GetFlyweight(i);
9: flyweight.Stand(1, i);
10: }
11: #endregion
12:
13: Console.ReadLine();
14: }