设计模式的书都很喜欢拿Prototype(原型)与Abstract Factory(抽象工厂)两个模式作比较。从相似的角度来说,它们两个都通过Composite的方式,把实例化的过程从具体使用这些对象的类中分离出去,只让用户知道调用Manager或Factory的哪些方法来实例化而不用记住具体的类名。抽象工厂和原型模式是基于相同原理的模式,唯一的不同之处就是实例化的过程,抽象工厂是基于new来实例化对象的(缺点是Hard Code,编译时已经决定了要实例化的具体是什么对象),而原型是基于Clone来创建对象的(优点是在程序运行时,能够像策略模式一样实时更换对象,创建出基于相同接口的不同对象。)我更倾向的观点把原型模式归为抽象工厂的一种具体实现形式,强调实例的创建时是基于另一个实例的Clone方法。
上面说了原型模式这么多好处。下面将按照《Head First设计模式》的例子的改版来做一下解说。
场景:
你接了某个游戏公司的项目,项目为设计一个角色扮演游戏。在游戏中,当英雄在动态创建的场景中闯荡的时候,免不了要与各色各样的怪物来战斗。不仅有游戏公司预先定制好的怪物,而且还能够让高级玩家手动创造新的怪物(预先定义好的怪物的特征几乎都不一样,行为比用户自定义的怪物要多,用户自定义的怪物只具备些简单行为,跟尾随在身后的吉祥物差不多。对于自定义的怪物,玩家能够选择怪物的类型,例如是Dragon还是Bird,并且为其选择颜色等)。Monster的设计如下:
凡是Monster都要实现IMonster的接口。DuckMonster和CakeMonster为游戏公司预设的怪物类,场景中每出现一只头种怪物,都需要用到new DuckMonster或new CakeMonster。通过下面的两个工厂之一来解耦其实例化的代码。
而blueEyesWhiteDragon和bedEyesBlackBird为用户定义的一种怪物(请注意!他们不是类,而是实例),场景中每出现一头这种怪物,都需要调一次blueEyesWhiteDragon.Clone()或bedEyesBlackBird.Clone()。
先说一下MonsterFactory1与MonsterFactory2。
MonsterFactory1的缺点:MonsterFactory1每当增加一种新怪物的时候,就要增加一个GetXXMonter方法,等于不断地对外开放接口,而且方法多起来难以管理。
或者说为DuckMonster和CakeMonster各自建一个Factory,好处是只需要一个Get方法。缺点自然是Factory太多,无法管理。
MonsterFactory2的缺点自然是每增加一种新怪物,就多一个if来根据type参数来取得实例,不符合面向对象思想的“对扩展开放,对修改关闭”的原则。
到了MonsterPrototypeManager,通过依赖注入(对于依赖注入不理解的可以看看我的另外两篇文章:理解Spring中的依赖注入以及利用Spring解耦VS的WebService),我们可以不用写任何初始化的代码,来完成实例的创建,并且注入到regMonsterList这个Dictionary类的实例中。想获得实例,只要把type传进去GetSpecificMonster,就能从regMonsterList中取得相应的实例,并返回Clone后的实例。
在游戏过程中,高级玩家创建了一种新的怪物。由于这种怪物是一个实例,而不是一个类,所以还可以动态添加到regMonsterList当中,需要的时候可以拿出来Clone一下,来创建新实例。而MonsterFactory们就只能在编译时期确定好具体定义好的怪物“类”,灵活性比Prototype模式的MonsterPrototypeManager要差。