用策略模式重构,然后应用SOLID原则
Refactor with strategy pattern and then apply SOLID principle
我在一个应用程序中有一个 C# class,正在寻找重构它的方法。
Send
方法在 class 中不存在。这是我想出的解决方案。
- 将来会有更多的电子邮件类型。
- 我不知道我是否可以在这里应用 SOLID Open/Closed 原则,因为添加新的电子邮件类型需要修改此 class。
- 这个服务的消费者不应该关心业务逻辑,而只需要知道新的
emailType
和customerId
。 EmailService
的消费者只知道要发送什么类型的电子邮件和 customerId
.
class EmailService
{
Send(int emailType, int customerId)
{
switch(emailType)
{
case 1: SendSignupEmail(customerId);
break;
case 2: SendOrderEmail(customerId);
break;
case 3: SendCancellationEmail(customerId);
break;
}
}
SendSignupEmail(int customerId);
SendOrderEmail(int customerId);
SendCancellationEmail(int customerId);
}
您可以将 switch
替换为 Dictionary
和 class 外部的一些配置:
定义一个表示发送方法签名的新接口:
interface ISendEmail
{
Send(int customerId);
}
为每个发送方法创建一个 class 来表示发送方法。
class SendSignupEmail : ISendEmail
{
public Send(int customerId){}
}
class SendOrderEmail : ISendEmail
{
public Send(int customerId){}
}
class SendCancellationEmail : ISendEmail
{
public Send(int customerId){}
}
这些是电子邮件策略。
现在 EmailService
只能成为将 emailType
路由到正确实现的一种方式,并且永远不需要为新的电子邮件类型进行更改 (OCP).
public interface IEmailService
{
void Send(int emailType, int customerId);
}
class EmailService : IEmailService
{
private readonly Dictionary<int, SendEmail> senders = new Dictionary<int, SendEmail>();
public Send(int emailType, int customerId)
{
SendEmail email;
if (senders.TryGetValue(emailType, out email)) //replaces the switch
{ //found the email type, delegate the sending to the registered instance
email.Send(customerId);
}
else
{
//unregistered email type, this is like a default case in a switch
}
}
public Register(int emailType, SendEmail sender)
{
senders.Add(emailType, sender);
}
}
然后您可以在系统中的某个时刻创建此服务并注册电子邮件实现:
var emailService = new EmailService();
emailService.Register(1, new SendSignupEmail());
emailService.Register(2, new SendOrderEmail());
emailService.Register(3, new SendCancellationEmail());
IEmailService iEmailService = emailService;
您应该重用此实现并将相同的实例作为 IEmailService
传递给客户端 (DIP)。这里使用的接口是ISP,因为他们不需要(也不能使用)classes Register
方法。
所以如您所见,新的电子邮件实现将只是一个新的 class 和一个新的注册行,实现 OCP:
emailService.Register(4, new SendSomeNewEmail(serviceItDependsOn));
注意 serviceItDependsOn
,因为我使用的是 DIP 我可以注入额外的服务,或者可能是电子邮件模板。无需修改客户端或 EmailService
.
即可处理新电子邮件所需的许多额外复杂性
这与通常的策略模式示例不同,因为路由到正确的策略,但它仍然外部化接口背后的工作,并且策略实现提供给 class。我认为这些是关键组成部分,所以我仍然 class 将其确定为策略模式。
策略模式要求您封装行为,以保持您的 类 原子性并专为单一目的而设计。
你应该做的是(我会在 Java 中写它,但在 C# 中必须非常相似):
interface Message {
void send(int customerId);
}
class SignupMessage implements Message {
// here you implement send method with specifics of signup behavior
}
class OrderMessage implements Message {
// here you implement send method with order specifics
}
class CancellationMessage implements Message {
// here you implement send method with cancellation specifics
}
class EmailService
{
void send(Message message, int customerId) {
message.send(customerId);
}
}
也可以说发送逻辑(连接到 POP 服务器并发送邮件)与消息本身无关。保持通用的代码不应该重新实现,所以我认为这个版本更有意义:
interface Message {
void getMessage(int customerId);
// I've assumed only messages are different between message types
}
// other classes are the same as above (only implementing "getMessage" this time)
class EmailService {
void send(Message message, int customerId) {
string msg = message->getMessage(customerId);
// what follows next is logic to send bessage
}
}
我在一个应用程序中有一个 C# class,正在寻找重构它的方法。
Send
方法在 class 中不存在。这是我想出的解决方案。- 将来会有更多的电子邮件类型。
- 我不知道我是否可以在这里应用 SOLID Open/Closed 原则,因为添加新的电子邮件类型需要修改此 class。
- 这个服务的消费者不应该关心业务逻辑,而只需要知道新的
emailType
和customerId
。EmailService
的消费者只知道要发送什么类型的电子邮件和customerId
.
class EmailService
{
Send(int emailType, int customerId)
{
switch(emailType)
{
case 1: SendSignupEmail(customerId);
break;
case 2: SendOrderEmail(customerId);
break;
case 3: SendCancellationEmail(customerId);
break;
}
}
SendSignupEmail(int customerId);
SendOrderEmail(int customerId);
SendCancellationEmail(int customerId);
}
您可以将 switch
替换为 Dictionary
和 class 外部的一些配置:
定义一个表示发送方法签名的新接口:
interface ISendEmail
{
Send(int customerId);
}
为每个发送方法创建一个 class 来表示发送方法。
class SendSignupEmail : ISendEmail
{
public Send(int customerId){}
}
class SendOrderEmail : ISendEmail
{
public Send(int customerId){}
}
class SendCancellationEmail : ISendEmail
{
public Send(int customerId){}
}
这些是电子邮件策略。
现在 EmailService
只能成为将 emailType
路由到正确实现的一种方式,并且永远不需要为新的电子邮件类型进行更改 (OCP).
public interface IEmailService
{
void Send(int emailType, int customerId);
}
class EmailService : IEmailService
{
private readonly Dictionary<int, SendEmail> senders = new Dictionary<int, SendEmail>();
public Send(int emailType, int customerId)
{
SendEmail email;
if (senders.TryGetValue(emailType, out email)) //replaces the switch
{ //found the email type, delegate the sending to the registered instance
email.Send(customerId);
}
else
{
//unregistered email type, this is like a default case in a switch
}
}
public Register(int emailType, SendEmail sender)
{
senders.Add(emailType, sender);
}
}
然后您可以在系统中的某个时刻创建此服务并注册电子邮件实现:
var emailService = new EmailService();
emailService.Register(1, new SendSignupEmail());
emailService.Register(2, new SendOrderEmail());
emailService.Register(3, new SendCancellationEmail());
IEmailService iEmailService = emailService;
您应该重用此实现并将相同的实例作为 IEmailService
传递给客户端 (DIP)。这里使用的接口是ISP,因为他们不需要(也不能使用)classes Register
方法。
所以如您所见,新的电子邮件实现将只是一个新的 class 和一个新的注册行,实现 OCP:
emailService.Register(4, new SendSomeNewEmail(serviceItDependsOn));
注意 serviceItDependsOn
,因为我使用的是 DIP 我可以注入额外的服务,或者可能是电子邮件模板。无需修改客户端或 EmailService
.
这与通常的策略模式示例不同,因为路由到正确的策略,但它仍然外部化接口背后的工作,并且策略实现提供给 class。我认为这些是关键组成部分,所以我仍然 class 将其确定为策略模式。
策略模式要求您封装行为,以保持您的 类 原子性并专为单一目的而设计。
你应该做的是(我会在 Java 中写它,但在 C# 中必须非常相似):
interface Message {
void send(int customerId);
}
class SignupMessage implements Message {
// here you implement send method with specifics of signup behavior
}
class OrderMessage implements Message {
// here you implement send method with order specifics
}
class CancellationMessage implements Message {
// here you implement send method with cancellation specifics
}
class EmailService
{
void send(Message message, int customerId) {
message.send(customerId);
}
}
也可以说发送逻辑(连接到 POP 服务器并发送邮件)与消息本身无关。保持通用的代码不应该重新实现,所以我认为这个版本更有意义:
interface Message {
void getMessage(int customerId);
// I've assumed only messages are different between message types
}
// other classes are the same as above (only implementing "getMessage" this time)
class EmailService {
void send(Message message, int customerId) {
string msg = message->getMessage(customerId);
// what follows next is logic to send bessage
}
}