使用策略模式重构,然后应用SOLID原则

时间:2017-03-04 14:28:36

标签: solid-principles strategy-pattern

我在应用程序中有一个如下所示的C#类,并且正在研究如何重构它。

  • 类中不存在Send方法。这是我提出的解决方案。
  • 将来会有更多电子邮件类型。
  • 我不知道我是否可以在此处应用SOLID打开/关闭原则,因为添加新的电子邮件类型需要修改此类。
  • 此服务的消费者不应该关注业务逻辑,而只需要知道新的emailTypecustomerIdEmailService的消费者只知道要发送的电子邮件类型和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);
}

2 个答案:

答案 0 :(得分:2)

策略模式要求您封装BEHAVIORS以使您的类保持原子性并为单一目的而设计。

你应该做的是(我将用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
    }
}

答案 1 :(得分:1)

您可以将switch替换为Dictionary以及该类外部的某些配置:

定义一个代表发送方法签名的新接口:

interface ISendEmail
{
    Send(int customerId);
}

对于每个send方法,创建一个表示send方法的类。

class SendSignupEmail : ISendEmail
{
    public Send(int customerId){}
}

class SendOrderEmail : ISendEmail
{
    public Send(int customerId){}
}

class SendCancellationEmail : ISendEmail
{
    public Send(int customerId){}
}

这些是电子邮件策略。

现在EmailService只能成为emailType路由到正确实现的一种方式,并且永远不需要更改新的emailTypes( 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 ,因为它们不需要(也不能使用)类Register方法。

正如您所看到的,新的电子邮件实施将只是一个新的类和一个新的注册行,实现 OCP

emailService.Register(4, new SendSomeNewEmail(serviceItDependsOn));

请注意serviceItDependsOn,因为我使用 DIP 我可以注入额外的服务,或者可能是电子邮件模板。无需修改客户端或EmailService即可处理新电子邮件所需的大量额外复杂性。

这与战略模式的通常示例不同,因为路由到正确的策略,但它仍然将界面背后的工作外部化,并且策略实现被提供给类。我认为这些是关键组件,所以我仍然将其归类为策略模式。