0 工厂方法模式简介
0.0 工厂方法模式定义
工厂方法模式是在基础上,为解决更复杂的对象创建问题而衍生进化出来的一种创建型模式。
工厂方法模式的核心思想是定义一个用于创建对象的接口,让其子类去决定去实例化哪个具体类的对象,工厂方法模式可以使一个类的实例化动作延迟到其子类。
工厂方法模式结构图如下
0.1 工厂方法模式应用场景
还接着祭坛生产英雄的示例,我们已经在一文中通过创建简单工厂方法类,来实现暗夜精灵种族4个英雄的创建
////// 创建英雄的静态方法 /// /// 英雄名称 ///public static IHero CreateHero(string heroName) { switch (heroName) { case "DH": return new DH(); case "WD": return new WD(); case "KOG": return new KOG(); case "POM": return new POM(); default: return null; } }
假设现在我们要创建不死族的英雄怎么办呢,如果依然使用简单工厂方法类,则首先需要实现不死族四个英雄类。
////// 死亡骑士/// public class DK : IHero{ ////// 秀出自己的技能 /// public void ShowSkills() { Console.WriteLine("我是死亡骑士,我会死亡缠绕、死亡契约、邪恶光环和操纵死尸。"); }}
////// 巫妖/// public class Lich : IHero{ ////// 秀出自己的技能 /// public void ShowSkills() { Console.WriteLine("我是巫妖,我会霜冻新星、寒冰甲、黑暗仪式和死亡凋零。"); }}
////// 地穴领主/// public class DL : IHero{ ////// 秀出自己的技能 /// public void ShowSkills() { Console.WriteLine("我是地穴领主,我会穿刺、刺盾、腐蚀甲虫和蝗群。"); }}
////// 恐惧魔王/// public class CL : IHero{ ////// 秀出自己的技能 /// public void ShowSkills() { Console.WriteLine("我是恐惧魔王,我会腐臭群蜂、睡眠、吸血光环、地狱火。"); }}
然后需要修改工厂方法,增加switch语句中的类型,将不死族四个英雄创建逻辑添加进去。
////// 创建英雄的静态方法/// /// 英雄名称///public static IHero CreateHero(string hero{ switch (heroName) { //暗夜精灵 case "DH": return new DH(); case "WD": return new WD(); case "KOG": return new KOG(); case "POM": return new POM(); // 不死族 case "DK": return new DK(); case "Lich": return new Lich(); case "CL": return new CL(); case "DL": return new DL(); default: return null; }}
还有兽族及人族,也需要如此修改。到此我们会感觉存在有以下问题
1、随着英雄的增多,简单工厂类需要反复修改。
2、简单工厂类过于庞大,职责混乱,负责了四个种族所有英雄的创建,而实际上,玩家在进入游戏时已经选好了自己的种族,只有可能创建所选种族的英雄。
我们到了这里,首先要想到的是,既然四个种族,分别有自己的祭坛,产生改种族的英雄,我们应该将简单工厂类按照种族进行职责拆分,此时参考上面提到的工厂方法模式定义以及结构图,我们会发现,现在是到了工厂方法模式出场的时候了。
1 工厂方法模式详解
1、提炼工厂方法接口
将原来的简单工厂类,进一步提炼为一个工厂方法接口,其包含一个名为CreateHero的接口。
////// 工厂方法接口/// public interface IFactory{ ////// 创建英雄的方法 /// /// 英雄名称 ///IHero CreateHero(string heroName);}
2、实现四个种族的工厂方法
四个种族创建英雄的工厂方法继承自工厂方法接口,实现CreateHero。
////// 暗夜精灵种族英雄工厂类/// public class NEFactory : IFactory{ ////// 创建英雄的静态方法 /// /// 英雄名称 ///public IHero CreateHero(string heroName) { switch (heroName) { //暗夜精灵 case "DH": return new DH(); case "WD": return new WD(); case "KOG": return new KOG(); case "POM": return new POM(); default: return null; } }}
////// 不死族英雄工厂类/// public class UDFactory : IFactory{ ////// 创建英雄的静态方法 /// /// 英雄名称 ///public IHero CreateHero(string heroName) { switch (heroName) { // 不死族 case "DK": return new DK(); case "Lich": return new Lich(); case "CL": return new CL(); case "DL": return new DL(); default: return null; } }}
3、客户端调用
static void Main(string[] args){ IFactory factory = new NEFactory(); // 初始化一个暗夜精灵族的英雄工厂 Console.WriteLine("我在开局时选择了暗夜精灵族,我的首发英雄是DH。"); IHero dh = factory.CreateHero("DH"); dh.ShowSkills(); factory = new UDFactory(); // 初始化一个不死族的英雄工厂 Console.WriteLine("我在开局时选择了不死族,我的首发英雄是DK。"); IHero dk = factory.CreateHero("DK"); dk.ShowSkills(); Console.ReadLine();}
4、通过反射实例化具体的工厂方法类
在实际应用中跟上面不同种族创建英雄的例子类似,一般在系统启动时就已经确定要使用哪种方法实例化工厂方法类,通常我们可以将工厂类的实例化通过配置文件的方式确定,从而避免源码的修改。
string factoryName = ConfigurationManager.AppSettings["FactoryName"]; // 读取配置文件IFactory factory = (IFactory)Assembly.Load("FactoryMethodPattern").CreateInstance("FactoryMethodPattern." + factoryName); // 实例化配置的工厂方法类
2 总结
工厂方法模式具有以下优点
1、更容易对现有功能进行扩展,如果有新的需求,只需要实现一个相应的工厂方法实现类即可,无需修改现有代码。
2、不同工厂方法类,实现了单一职责的设计原则。
工厂方法模式的缺点
由于具体的对象由具体指定的工厂方法类创建,导致具体产品和工厂方法类之间具有较强的耦合性