记录使用工厂模式加策略模式解决有多个不同邮件内容的难管理的痛点

记录使用工厂模式加策略模式解决有多个不同邮件内容的难管理的痛点

那天我正在写一个发送邮件验证码的功能,但是我开始的时候一出手就是if else,这样来判断应该发送什么内容的邮件,
后来我发现这样写太难维护了,然后我又换成了switch case,这样来判断应该发送什么内容的邮件,但是我觉得写太普通了,
所以我又换成了工厂模式加策略模式,这样来判断应该发送什么内容的邮件,这样就很方便了,而且也很好维护。后面需要新增邮件内容的时候,
只需要新增一个策略类,然后在工厂类的字典里面添加新的映射就行。所以我感觉设计模式还是很重要的,让代码更容易维护。不然如果使用if else,
那就需要修改很多地方。

项目文件

appsettings.json
IEmailStrategy.cs
IEmailStrategyFactory.cs
IMailSendService.cs
EmailStrategyFactory.cs
MailSendService.cs
CaptchaConfig.cs // 验证码配置
MailSendServiceConfig.cs // 邮件发送服务配置

appsettings.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"ConnectionStrings": {
"DefaultConnection": ""
},
"MailSendServiceConfig": {
"Host": "smtp.qq.com",
"Port": 587,
"EmailLocalAddress": "",
"keyword": ""
},
"SessionConfigure": {
"ExpirationTime": "3"
},
"CaptchaConfig": {
"CaptchaExpirationTime": "5"
},
"AllowedHosts": "*"
}

IEmailStrategy.cs

邮箱策略接口
后面如果需要新增邮件类型,只需要新增一个策略类,然后在工厂类的字典里面添加新的映射就行

1
2
3
4
5
6
7
8
9
namespace DaiBanShiWuList01x02.Interfaces
{
public interface IEmailStrategy
{
string Subject { get; }
string BuildBody(string captchaSTR, string effectiveTime);
}
}

IEmailStrategyFactory.cs

邮箱策略工厂接口

1
2
3
4
5
6
7
8
namespace DaiBanShiWuList01x02.Interfaces
{
public interface IEmailStrategyFactory
{
IEmailStrategy GetEmailStrategy(string emailType);
}
}

IMailSendService.cs

邮件发送服务接口

1
2
3
4
5
6
7
8
9
namespace DaiBanShiWuList01x02.Interfaces
{
public interface IMailSendService
{
//emailType:AccountRegister,PasswordForget,FriendVerification该参数用于策略模式选择不同的邮件模板,2025-10-4
void SendMailSendService(string SendToMailAdd, string captchaSTR, string emailType);
}
}

EmailStrategyFactory.cs

邮箱策略工厂

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
using DaiBanShiWuList01x02.Interfaces;
using DaiBanShiWuList01x02.Services.EmailStrategys;
namespace DaiBanShiWuList01x02.Services
{
public class EmailStrategyFactory: IEmailStrategyFactory
{
private readonly Dictionary<string, IEmailStrategy> _emailStrategies;

public EmailStrategyFactory()
{
// 目前是字典映射,后面会改成通过配置文件添加,2025-10-10
//后面可以在加新的邮件类型,即把待办事项列表通过邮件发送给用户,提醒用户有哪些待办事项,2025-11-28
//这种情况策略模式的好处就体现出来了,只需要新增一个策略类,然后在这里注册即可2025-11-28
_emailStrategies = new Dictionary<string, IEmailStrategy>
{
["AccountRegister"] = new AccountRegisterEmailStrategy(),
["PasswordForget"] = new PasswordForgetEmailStrategy(),
["FriendVerification"] = new FriendVerificationEmailStrategy(),
["AccountLogin"] = new AccountLoginEmailStrategy(),//2025-12-01,新增邮箱验证码登录
["ChangeAccountName"] = new ChangeAccountNameEmailStrategy()//2025-12-02,修改账号名称
};
}

public IEmailStrategy GetEmailStrategy(string emailType)
{
_emailStrategies.TryGetValue(emailType, out var strategy);
return strategy;
}
}
}

MailSendService.cs

邮件发送服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
using DaiBanShiWuList01x02.Interfaces;
using Microsoft.Extensions.Options;
using System.Net.Mail;
using System.Net;
namespace DaiBanShiWuList01x02.Services
{
public class MailSendService: IMailSendService
{

//使用策略模式处理不同类型的邮件,相关代码已转移到文件夹Services/EmailStrategys,2025-10-4

private readonly IOptionsMonitor<MailSendServiceConfig> _mailSendServiceConfig;
private readonly IOptionsMonitor<CaptchaConfig> _captchaConfig;//2025-12-02
private readonly IEmailStrategyFactory _emailStrategyFactory;//这里我改了,原来是用的IEmailStrategyFactory,现在直接用工厂,2025-11-28,又改为使用接口
public MailSendService(IOptionsMonitor<MailSendServiceConfig> mailSendServiceConfig, IEmailStrategyFactory emailStrategyFactory, IOptionsMonitor<CaptchaConfig> captchaConfig)
{
_mailSendServiceConfig = mailSendServiceConfig;
_emailStrategyFactory = emailStrategyFactory;
_captchaConfig = captchaConfig;
}

public void SendMailSendService(string SendToMailAdd, string captchaSTR, string emailType)//向该邮箱(SendToMailAdd)发送验证码(captchaSTR)
{
//目前已有类型(3个,注意为字符串)
//"AccountRegister"
//"PasswordForget"
//"FriendVerification"
//"AccountLogin"//2025-12-01,新增邮箱验证码登录
var emailStrategy = _emailStrategyFactory.GetEmailStrategy(emailType);//2025-10-4,策略待实现。new 已经实现2025-10-26

MailMessage mailMessage = new MailMessage
{
From = new MailAddress(_mailSendServiceConfig.CurrentValue.EmailLocalAddress, "ClipFlow", System.Text.Encoding.UTF8),
To = { new MailAddress(SendToMailAdd) },
Bcc = { new MailAddress(SendToMailAdd) },
Subject = emailStrategy.Subject,
SubjectEncoding = System.Text.Encoding.UTF8,
IsBodyHtml = true,
Body = $@"{emailStrategy.BuildBody(captchaSTR,_captchaConfig.CurrentValue.CaptchaExpirationTime)}",
BodyEncoding = System.Text.Encoding.UTF8
};

SmtpClient smtpClient = new SmtpClient(_mailSendServiceConfig.CurrentValue.Host, _mailSendServiceConfig.CurrentValue.Port);
smtpClient.EnableSsl = true;
smtpClient.Credentials = new NetworkCredential(_mailSendServiceConfig.CurrentValue.EmailLocalAddress, _mailSendServiceConfig.CurrentValue.keyword);
try
{
smtpClient.Send(mailMessage);
}
catch (Exception ex)
{
Console.WriteLine($"邮件发送失败: {ex.Message}");
throw;
}
}
}
}

CaptchaConfig.cs

验证码配置

1
2
3
4
5
6
7
8
namespace DaiBanShiWuList01x02
{
public class CaptchaConfig
{
public string CaptchaExpirationTime { get; set; }
}
}

MailSendServiceConfig.cs

邮件发送服务配置

1
2
3
4
5
6
7
8
9
10
11
namespace DaiBanShiWuList01x02
{
public class MailSendServiceConfig
{
public string Host { get; set; }
public int Port { get; set; }
public string EmailLocalAddress { get; set; }
public string keyword { get; set; }
}
}

使用

在控制器里面声明属性:

private readonly IMailSendService _mailSendService;
然后在构造函数中注入:

1
2
3
4
public HomeController(IMailSendService mailSendService)
{
_mailSendService = mailSendService;
}

在控制器方法中使用:

1
_mailSendService.SendMailSendService(userAccount.Email, loginCaptchaSTR, "AccountLogin");