简单工厂模式
创建一个工厂类,对实现了同一个接口的多个类进行实例的创建。
//抽象类 人 public abstract class HuMan { public abstract void Talk(); } //黑人实现类 public class BlackHuman : HuMan { public override void Talk() { Console.WriteLine("I am BlackHuman"); } } //白人实现类 public class WhiteHuman : HuMan { public override void Talk() { Console.WriteLine("I am WhiteHuman"); } } /// <summary> /// 简单工厂 /// </summary> public static class HumanFactory { public static HuMan CreateHuman(HumanEnum humanEnum) { HuMan huMan = null; switch (humanEnum) { case HumanEnum.Black: huMan = new BlackHuman(); break; case HumanEnum.White: huMan = new WhiteHuman(); break; default: throw new NullReferenceException(); } return huMan; } } //客户端调用 //创建一个白人 var whiteHuman1 = SimpleFactory.CreateHuman(HumanEnum.White); whiteHuman1.Talk(); //创建一个黑人 var blackHuman1 = SimpleFactory.CreateHuman(HumanEnum.Black); blackHuman1.Talk();
优点:工厂中包含了必要的逻辑判断,根据客户端的选择条件动态的创建相关的类,对于客户端来说,它只需要提供创建实现类的参数,隐藏了创建实例的细节,去除了对具体实现类的依赖。
缺点:当要新增一个实现类时,需要修改工厂类的代码(switch下增加一个Case),违背了开放-封闭原则。
当然上面的代码其实还存在很大的优化空间,我们可以先看下工厂模式的实现方案,对比下两种模式上的区别。
工厂模式
定义一个用于创建对象的接口,让子类决定实现哪个类。工厂方法将创建对象实例的职责移交给了子类。
//定义一个创建人类的工厂接口 public interface IHumanFactory { HuMan CreateHuman(); } //定义一个创建白人的工厂 public class WhiteHumanFactory : IHumanFactory { public HuMan CreateHuman() { return new WhiteHuman(); } } //定义一个创建黑人的工厂 public class BlackHumanFactory : IHumanFactory { public HuMan CreateHuman() { return new BlackHuman(); } } //客户端调用 var whiteHumanFactory = new WhiteHumanFactory(); var whiteHuman2 = whiteHumanFactory .CreateHuman(); whiteHuman2.Talk(); var blackHumanFactory = new BlackHumanFactory(); var blackHuman2 = blackHumanFactory .CreateHuman(); blackHuman2.Talk();
优点:当要新增一个实现类时,只需要再增加一个实现类和创建实现类的工厂,不需要修改原有工厂的代码,保留了简单工厂对创建实例的封装,又体现了开放-封闭原则。
缺点:客户端需要决定实例化哪一个工厂来创建实现类,增加了客户端的复杂度。同时每增加一个实现类,也要增加一个工厂类,增加了代码复杂度。
简单工厂+反射
可以通过反射技术来强化简单工厂,让简单工厂同样符合开放封闭原则。
/// <summary> /// 简单工厂 /// </summary> public static class HumanFactory { public static HuMan CreateHuman(string humanType) { var a = Assembly.LoadFrom($"{AppDomain.CurrentDomain.BaseDirectory}DesignPatterns.Model.dll"); return (HuMan)a.CreateInstance($"DesignPatterns.Model.Factory.{humanType}"); } }
客户端可以通过配置文件或者数据库中获取humanType(在程序或者数据库中维护一个参数类型+类名的字典),通过反射技术动态的创建实例。
简单工厂+依赖注入
//工厂类 public class HumanFactory { private readonly Func<HumanEnum, Human> _func; public HumanFactory(Func<HumanEnum, Human> func) { this._func = func; } public Human CreateHuman(HumanEnum humanEnum) { return _func(humanEnum); } } public static class HunmanDependencyInjection { //维护一个实现类的字典 private static Dictionary<HumanEnum, Type> dicHuman = new Dictionary<QRCodeType, Type>() { { HumanEnum.White,typeof(WhiteHuman)}, { HumanEnum.Black,typeof(BlackHuman)} }; public static void AddHuman(this IServiceCollection serviceCollection) { serviceCollection.AddScoped<WhiteHuman>(); serviceCollection.AddScoped<BlackHuman>(); serviceCollection.AddScoped(factory=> { Func<HumanEnum, Human> func = humanType=> { return (Human)factory.GetService(dicHuman [humanType]); }; return func; }); serviceCollection.AddScoped<HumanFactory>(); } } //在Startup中注入实现类和工厂 public void ConfigureServices(IServiceCollection services) { services.AddHuman(); services.AddMvc(); } //客户端在构造函数中引入HumanFactory var whiteHuman = HumanFactory.CreateHuman(HumanEnum.White); whiteHuman.Talk();
这样我们就用Ioc实现了一个简单工厂,上面的代码示例是将实现类的字典放在工厂中维护,其实可以将字典放到配置文件或者数据库中维护,这样我们再增加新的实现类时,就不需要在修改工厂的代码,实现了实现类的动态扩展。也体现了开闭原则。