设计模式-简单工厂模式
一、最开始的代码
1、种族接口,有一个展示种族英雄的方法
public interface IRace { void ShowKing(); }
2、定义3个种族,实现接口
public class Human : IRace { public void ShowKing() { Console.WriteLine("The King of {0} is {1}", this.GetType().Name, "Sky"); } }
public class NE : IRace { public void ShowKing() { Console.WriteLine("The King of {0} is {1}", this.GetType().Name, "Moon"); } }
public class ORC : IRace { public void ShowKing() { Console.WriteLine("The King of {0} is {1}", this.GetType().Name, "Grubby"); } }
3、定义玩家,玩家可以玩不同种族的英雄
public class Player { public int Id { get; set; } public string Name { get; set; } public void PlayHuman(Human human) { Console.WriteLine("******************************"); Console.WriteLine("This is {0} Play War3.{1}", this.Name, human.GetType().Name); human.ShowKing(); } public void PlayNE(NE ne) { Console.WriteLine("******************************"); Console.WriteLine("This is {0} Play War3.{1}", this.Name, ne.GetType().Name); ne.ShowKing(); } public void PlayORC(ORC orc) { Console.WriteLine("******************************"); Console.WriteLine("This is {0} Play War3.{1}", this.Name, orc.GetType().Name); orc.ShowKing(); } }
4、使用方式
Player player = new Player() { Id = 123, Name = "Eleven" }; Human race = new Human();//1 最原始最习惯的 player.PlayHuman(race); ORC oRC = new ORC(); player.PlayORC(oRC);
这种方式,想要使用哪个种族都需要new具体的种族、修改代码来调用不同方法,如果新增了种族,Player类就必须新增新方法PlayNewRace()。这样依赖了细节,不易扩展,需要经常修改代码
依赖倒置原则:上层模块不应该依赖于下层模块,二者应该通过抽象来依赖。
换句话说就是:依赖抽象,而不是依赖细节
二、依赖抽象
1、玩家类新增一个方法,该方法可以接收所有的种族,该方法的形参为IRace,可接收实现了IRace接口的类实例。
public void Play(IRace iRace) { Console.WriteLine("******************************"); Console.WriteLine("This is {0} Play War3.{1}", this.Name, iRace.GetType().Name); iRace.ShowKing(); }
2、使用方式
IRace race = new Human();//2 左边换成抽象 依赖倒置原则 player.Play(race); IRace race2 = new ORC(); player.Play(race2);
一个方法可以使用所有的种族了。但左边(IRace race = )换成抽象了,右边(new Human();)还是依赖了细节,因为//new IRace();编译不通过,接口不能直接实例化,
那问题出来了,既不想依赖细节,又需要对象,引出工厂。
三、简单工厂模式
1、概念:包含一组需要创建的对象,通过一个工厂类来实例化对象
2、使用枚举参数指定要创建的种族
public class ObjectFactory { public static IRace CreateInstance(RaceType raceType) { IRace race = null; switch (raceType) { case RaceType.Human: race = new Human(); break; case RaceType.NE: race = new NE(); break; case RaceType.ORC: race = new ORC(); break; default: throw new Exception("wrong raceType"); } return race; } } public enum RaceType { Human, NE, ORC }
使用方式
IRace race = ObjectFactory.CreateInstance(RaceType.Human);
player.Play(race);
但是如果现在要换成使用ORC种族,则又要修改代码,变成
IRace race = ObjectFactory.CreateInstance(RaceType.ORC);
player.Play(race);
下面使用配置文件的方法,来传入要使用的种族
3、使用配置文件配合简单工厂模式
配置文件App.config新增配置项节点
<appSettings> <add key="IRaceType" value="Human"/> </appSettings>
ObjectFactory工厂类新增方法
private static string IRaceType = ConfigurationManager.AppSettings["IRaceType"]; public static IRace CreateInstanceConfig() { RaceType raceType = (RaceType)Enum.Parse(typeof(RaceType), IRaceType); return CreateInstance(raceType); }
使用方法
IRace race = ObjectFactory.CreateInstanceConfig();
player.Play(race);
这样如果要改变使用的种族,就不必要修改代码了,直接修改配置文件的IRaceType的value值。
但如果现在要新增一个新种族Undead不死族,首先新增Undead类
namespace FactoryPattern.War3.Service { /// <summary> /// War3种族之一 /// </summary> public class Undead : IRace { public void ShowKing() { Console.WriteLine("The King of {0} is {1}", this.GetType().Name, "GoStop"); } } }
然后还需要修改代码,RaceType枚举新增Undead
public enum RaceType { Human, NE, ORC, Undead }
undead可以通过类库引入,不用修改现有代码,但是还是要修改修改RaceType枚举。
现在再升级为通过反射来创建种族实例,这样就可以不用修改RaceType枚举了
4、使用反射配合简单工厂模式
配置文件App.config新增配置项节点
<appSettings> <add key="IRaceTypeReflection" value="FactoryPattern.War3.Service.Undead,FactoryPattern.War3.Service"/> </appSettings>
ObjectFactory工厂类新增方法
private static string IRaceTypeReflection = ConfigurationManager.AppSettings["IRaceTypeReflection"]; public static IRace CreateInstanceConfigReflection() { Assembly assembly = Assembly.Load(IRaceTypeReflection.Split(',')[1]); Type type = assembly.GetType(IRaceTypeReflection.Split(',')[0]); return (IRace)Activator.CreateInstance(type); }
这样以后只要修改app.config里的配置项,上端就能不修改代码也可以使用不同种族了,还支持新增种族。
四、问题
今天只是创建Race,就需要一个方法;那项目中有N多个接口,难道每个接口都去创建一个工厂方法吗?
简单工厂模式还不能支持,需要抽象工厂模式,或者ioc容器