使用策略模式为一个事件实现两个通知系统

Implement two notification systems for an event with Strategy pattern

我的数据库中有一个订阅列表

Subscriber:
string Name      <- required
string Phone     <- required
string EventName <- required
string Email     <- optional 

订阅者可以通过其名称订阅某些事件 (EventName)。

当事件发生时,我需要通过电子邮件或短信向所有订阅者(EventName==the event which occurred)发送通知。如果 Email 不为空,则发送电子邮件。如果 Email 为空,发送短信到 Phone.

我的业务逻辑我创建了这个抽象:

public interface INotification
{
   Task Send(string message, IEnumerable<Subscriber> subscribers);
}

并在事件处理程序中使用它

var subscribes = _db.Subscribers.Where(x => x.EventName == event);
await _notification.Send(message, subscribes);

然后我将使用策略模式。

public interface INotificationStartegy
{
   Task Send(string message, string to);
}
public interface EmailNotificationStartegy : INotificationStartegy
{
   /// 
}
public interface SmsNotificationStartegy : INotificationStartegy
{
   /// 
}

这里有一个 INotification 实现的例子

public class Notification : INotification
{
  public Notification(SmsNotificationStartegy smsSender, EmailNotificationStartegy emailSender) =>
     (_smsSender, _emailSender);

  public async Task Send(string message, IEnumerable<Subscriber> subscribers)
  {   
    foreach(Subscriber sub in subscribers)
    {
        if (!string.IsNullOrEmpty(sub.Email)
        {
            await _emailSender.Send(message, sub.Email);
        } else {
            await _smsSender.Send(message, sub.Phone);
        }
    }
  }
}

能否请您建议这是否是使用策略的正确方法?我认为 Strategy 模式可以帮助我们在代码中避免 if/switch,但是谁来决定使用什么策略呢?我很困惑,我真的需要这个 INotification 抽象还是可以直接在我的事件处理程序中使用 forearch

谁能解释一下 INotification 的好处?或者它是一个错误的实现?

如果我需要添加一个策略,我应该总是修改 Notification 怎么办?有什么方法可以避免吗?

我认为你误解了策略模式。在您的实现中,您正在创建单个具体的 notification 对象,该对象应该处理您尝试为其实现此模式的多个函数。 所以在我看来,你会有一个单一的通知策略,你可以通过构造函数向该策略注入一个 INotification 对象的单一具体实现,无论是 EmailNotification 还是 SmsNotification.

让多个可能的接收者可以决定他们是否可以发送通知将是中介模式的工作。

如果您真的不想使用调解器,那么最简单的解决方案是创建多个 NotificationStrategy 对象,每个对象都注入一个 INotification 并使用/迭代它们 - 2 种不同的通知类型= 2 个策略对象,每个对象都注入了不同的 INotification 实现。

多亏了这一点,不再需要 if-elsing - 每个 INotification 实现都会自行决定是否可以根据给定数据的特定方式进行通知。

另一种选择(有一些 if-else 但或多或少与策略模式兼容)是首先分析给定的数据,然后,当您知道自己拥有什么时,创建适当的策略对象并注入足够的 INotification具体对象。

编辑具有多个调度程序的策略模式的最小工作示例:

using System.Threading.Tasks;
using System.Collections.Generic;
                    
public class Program
{
    public static async Task Main()
    {
        var subscribers = new List<Subscriber>(); // Data of subscribers - empty just for simplicity
        
        // Decide which strategy is good - sms, email or both?
        // use if-else, switch, pattern matching, external service or something else
        // Combine subscribers for each strategy into lists / other collections
        var smsSubscribers = new List<Subscriber>();
        var emailSubscribers = new List<Subscriber>();
        
        var smsDispatcher = new Dispatcher(new SmsStrategy());
        var emailDispatcher = new Dispatcher(new EmailStrategy());
        
        foreach(var entry in smsSubscribers)
        {
            await smsDispatcher.Send("message...", smsSubscribers);
        }
        
        foreach(var entry in emailSubscribers)
        {
            await emailDispatcher.Send("message...", smsSubscribers);
        }   
    }
    
    public class Dispatcher
    {
        INotificationStrategy _strategy;
        
        public Dispatcher(INotificationStrategy strategy)
        {
            _strategy = strategy;
        }
        
        public Task Send(string message, IEnumerable<Subscriber> subscribers)
        {
            return _strategy.Send(message, subscribers);
        }
    }
    
    public interface INotificationStrategy
    {
       Task Send(string message, IEnumerable<Subscriber> subscribers);
    }
    
    public class EmailStrategy : INotificationStrategy
    {
        public Task Send(string message, IEnumerable<Subscriber> subscribers)
        {
            // Check data, iterate over subscribers, decide if sending EMAIL is possible etc and SEND.
            return Task.CompletedTask; // Only here to remove compiler error
        }
    }
    
    public class SmsStrategy : INotificationStrategy
    {
        public Task Send(string message, IEnumerable<Subscriber> subscribers)
        {
            // Check data, iterate over subscribers, decide if sending SMS is possible etc and SEND.
            return Task.CompletedTask; // Only here to remove compiler error
        }
    }
    
    public class Subscriber
    {
        // Some subscriber data...
    }
}

注意,对于多个收件人,mediator 模式会更有用 - 查看 mediatr 项目。