工厂模式和工厂方法模式
工厂模式和工厂方法模式是设计模式中较为常见的两种模式,借助于依赖注入可以更好的发挥模式的特性。本文将通过一个业务需求的变化过程来阐述如何更好的使用设计模式与依赖注入。
业务需求
在用户登录之后,需要向用户发送一条短信,通知用户。
代码演练
公共内容
namespace Use_Dependency_Injection_With_Factory_Pattern
{
/// <summary>
/// 发送短信
/// </summary>
public interface ISmsSender
{
/// <summary>
/// 发送短信
/// </summary>
/// <param name="phone">手机</param>
/// <param name="message">短信 </param>
void Send(string phone, string message);
}
/// <summary>
/// 用户业务
/// </summary>
public interface IUserBll
{
/// <summary>
/// 登录
/// </summary>
/// <param name="phone"></param>
/// <param name="password"></param>
bool Login(string phone, string password);
}
public interface IUserDal
{
bool Exists(string phone, string password);
}
public class UserDal : IUserDal
{
private const string StaticPassword = "newbe";
private const string StaticPhone = "yueluo";
public bool Exists(string phone, string password)
{
// 使用固定的账号密码验证
return phone == StaticPhone && password == StaticPassword;
}
}
}
公共内容中包含了业务需求中的主要接口ISmsSender和IUserBll,以及演示用的数据存储层接口与实现。
版本1,使用控制台“发送短信”
using Autofac;
using System;
namespace Use_Dependency_Injection_With_Factory_Pattern
{
public static class Demo1
{
public static void Run()
{
Console.WriteLine($"开始运行{nameof(Demo1)}");
var cb = new ContainerBuilder();
cb.RegisterType<UserDal>().As<IUserDal>();
cb.RegisterType<UserBll>().As<IUserBll>();
cb.RegisterType<ConsoleSmsSender>().As<ISmsSender>();
var container = cb.Build();
var userBll = container.Resolve<IUserBll>();
var login = userBll.Login("yueluo", "newbe");
Console.WriteLine(login);
login = userBll.Login("newbe", "yueluo");
Console.WriteLine(login);
Console.WriteLine($"结束运行{nameof(Demo1)}");
}
public class UserBll : IUserBll
{
private readonly IUserDal _userDal;
private readonly ISmsSender _smsSender;
public UserBll(
IUserDal userDal,
ISmsSender smsSender)
{
_userDal = userDal;
_smsSender = smsSender;
}
public bool Login(string phone, string password)
{
var re = _userDal.Exists(phone, password);
if (re)
{
_smsSender.Send(phone, "您已成功登录系统");
}
return re;
}
}
public class ConsoleSmsSender : ISmsSender
{
public void Send(string phone, string message)
{
Console.WriteLine($"已给{phone}发送消息:{message}");
}
}
}
}
简要分析。版本1使用构造函数注入实现了代码的解耦,使用Autofac作为容器管理,常规用法,没有问题。
由于没有正常的短信发送调用,所以使用ConsoleSmsSender在控制台中输出消息进行模拟发送。
版本2,线上版本“真发短信”,开发版本“假发短信”
using Autofac;
using System;
namespace Use_Dependency_Injection_With_Factory_Pattern
{
public static class Demo2
{
public static void Run()
{
Console.WriteLine($"开始运行{nameof(Demo2)}");
var cb = new ContainerBuilder();
cb.RegisterType<UserDal>().As<IUserDal>();
cb.RegisterType<UserBll>().As<IUserBll>();
// 使用预编译命令, 使得 Release 和 Debug 版本注册的对象不同,从而实现调用的短信API不同
#if DEBUG
cb.RegisterType<ConsoleSmsSender>().As<ISmsSender>();
#else
cb.RegisterType<HttpApiSmsSender>().As<ISmsSender>();
#endif
var container = cb.Build();
var userBll = container.Resolve<IUserBll>();
var login = userBll.Login("yueluo", "newbe");
Console.WriteLine(login);
login = userBll.Login("newbe", "yueluo");
Console.WriteLine(login);
Console.WriteLine($"结束运行{nameof(Demo2)}");
}
public class UserBll : IUserBll
{
private readonly IUserDal _userDal;
private readonly ISmsSender _smsSender;
public UserBll(
IUserDal userDal,
ISmsSender smsSender)
{
_userDal = userDal;
_smsSender = smsSender;
}
public bool Login(string phone, string password)
{
var re = _userDal.Exists(phone, password);
if (re)
{
_smsSender.Send(phone, "您已成功登录系统");
}
return re;
}
}
/// <summary>
/// 调试·短信API
/// </summary>
public class ConsoleSmsSender : ISmsSender
{
public void Send(string phone, string message)
{
Console.WriteLine($"已给{phone}发送消息:{message}");
}
}
/// <summary>
/// 真·短信API
/// </summary>
public class HttpApiSmsSender : ISmsSender
{
public void Send(string phone, string message)
{
Console.WriteLine($"已调用API给{phone}发送消息:{message}");
}
}
}
}
简要分析。与版本1相比,增加了真实调用短信API的实现类,并且通过预编译的方式,实现了Debug模式和Release模式下发布不同版本的效果。一定程度上已经完成了需求。
版本3,引入工厂模式,实现“短信API选择”逻辑的分类
using Autofac;
using System;
namespace Use_Dependency_Injection_With_Factory_Pattern
{
public static class Demo3
{
public static void Run()
{
Console.WriteLine($"开始运行{nameof(Demo3)}");
var cb = new ContainerBuilder();
cb.RegisterType<UserDal>().As<IUserDal>();
cb.RegisterType<UserBll>().As<IUserBll>();
cb.RegisterType<SmsSenderFactory>().As<ISmsSenderFactory>();
cb.RegisterType<ConfigProvider>().As<IConfigProvider>();
var container = cb.Build();
var userBll = container.Resolve<IUserBll>();
var login = userBll.Login("yueluo", "newbe");
Console.WriteLine(login);
login = userBll.Login("newbe", "yueluo");
Console.WriteLine(login);
Console.WriteLine($"结束运行{nameof(Demo3)}");
}
public class UserBll : IUserBll
{
private readonly IUserDal _userDal;
private readonly ISmsSenderFactory _smsSenderFactory;
public UserBll(
IUserDal userDal,
ISmsSenderFactory smsSenderFactory)
{
_userDal = userDal;
_smsSenderFactory = smsSenderFactory;
}
public bool Login(string phone, string password)
{
var re = _userDal.Exists(phone, password);
if (re)
{
var smsSender = _smsSenderFactory.Create();
smsSender.Send(phone, "您已成功登录系统");
}
return re;
}
}
/// <summary>
/// 调试·短信API
/// </summary>
public class ConsoleSmsSender : ISmsSender
{
public void Send(string phone, string message)
{
Console.WriteLine($"已给{phone}