i'm trying to implement the decorator pattern in C#. I'm creating a notification system that decorates the notification to be sent at runtime. I have this classes : `
namespace NotificationHub.Domain.Hubs
{
public interface INotificationHub
{
public Task SendMessage(Notification notification);
}
}
and it's implemented by 4 classes : BasicNotificationHub
namespace NotificationHub.Domain.Hubs
{
public class BasicNotificationHub : INotificationHub
{
public async Task SendMessage(Notification notification)
{
await Task.FromResult(0);
Console.WriteLine($"Basic Notification Behavior Called for {notification.ProductID}");
}
}
}
SignalR
public class SignalRDecorator : INotificationHub
{
private readonly IHubContext<ProductNotificationHub, INotificationHub> _signalrNotificator;
private readonly INotificationHub _notificationHub;
public SignalRDecorator(IHubContext<ProductNotificationHub, INotificationHub> hubContext, INotificationHub notificationHub)
{
_signalrNotificator = hubContext;
_notificationHub = notificationHub;
}
public async Task SendMessage(Notification notification)
{
await _notificationHub.SendMessage(notification);
await _signalrNotificator.Clients.Group(notification.ProductID).SendMessage(notification);
}
}
SmsNotification
public class SmsDecorator : INotificationHub
{
private readonly IUserService _userService;
private readonly INotificationHub _notificationHub;
public SmsDecorator(IUserService userService, INotificationHub notificationHub)
{
_userService = userService;
_notificationHub = notificationHub;
}
public async Task SendMessage(Notification notification)
{
List<string> phoneNumbers = await _userService.GetUserPhonenNumbersSubscribedToProduct(notification.ProductID);
await _notificationHub.SendMessage(notification);
await SendMultipleSms(phoneNumbers, notification.ProductID, notification.ProductName);
}
private async Task SendMultipleSms(List<string> phoneNumbers, string productID, string productName)
{
foreach (string phoneNumber in phoneNumbers)
{
await SendSms(phoneNumber, productID, productName);
}
}
private async Task SendSms(string phoneNumber, string productId, string productName)
{
await Task.FromResult(0);
Console.WriteLine($"Sending Sms to {phoneNumber} about product {productId} {productName}");
}
}
And Mailing
public class MailDecorator : INotificationHub
{
private readonly IUserService _userService;
private readonly INotificationHub _notificationHub;
public MailDecorator(IUserService userService, INotificationHub notificationHub)
{
_userService = userService;
_notificationHub = notificationHub;
}
public async Task SendMessage(Notification notification)
{
List<string> mailsList = await _userService.GetUserMailsSubscribedToProduct(notification.ProductID);
await _notificationHub.SendMessage(notification);
await SendMultipleEmailAsync(mailsList, notification);
}
private async Task SendMultipleEmailAsync(List<string> mailsList, Notification notification)
{
foreach (string mailAdress in mailsList)
{
await SendEmailAsync(notification, mailAdress);
}
}
private async Task SendEmailAsync(Notification notification, string mailAdress)
{
await Task.FromResult(0);
Console.WriteLine($"Mail sent for product {notification.ProductID} to user {mailAdress}");
}
}
`
And the decoration happens in my service : `
public class InMemoryProductService : IProductService
{
private readonly List<Product> _products;
private IServiceProvider _serviceProvider;
public InMemoryProductService(IServiceProvider serviceProvider)
{
_products = new List<Product>
{
new Product
{
ProductId="P01",
ProductName="Cool Product",
Description="World coolest Product",
Price=9.99m,
Stock=10,
OnSale=false
},
new Product
{
ProductId="P02",
ProductName="Cool Expensive Product",
Description="World coolest expensive Product",
Price=999.99m,
Stock=0,
OnSale=false
}
};
_serviceProvider = serviceProvider;
}
public Task<List<Product>> GetProducts()
{
return Task.FromResult(_products);
}
public async Task UpdateProduct(Product product)
{
var foundProduct = _products.FirstOrDefault(x => x.ProductId == product.ProductId);
if (foundProduct != null)
{
INotificationHub notificationHub = GetNotificationHubs(product, foundProduct);
await notificationHub.SendMessage(new Notification
{
ProductID = product.ProductId,
ProductName = product.ProductName,
Message = "i did it"
});
}
}
private INotificationHub GetNotificationHubs(Product product, Product foundProduct)
{
INotificationHub notificationHub = new BasicNotificationHub();
if (product.Stock > foundProduct.Stock)
{
notificationHub = new SignalRDecorator((IHubContext<ProductNotificationHub, INotificationHub>)_serviceProvider.GetService(typeof(IHubContext<ProductNotificationHub, INotificationHub>)), notificationHub);
}
if (product.Price != foundProduct.Price)
{
notificationHub = new MailDecorator((IUserService)_serviceProvider.GetService(typeof(IUserService)), notificationHub);
}
if (product.OnSale != foundProduct.OnSale)
{
notificationHub = new SmsDecorator((IUserService)_serviceProvider.GetService(typeof(IUserService)), notificationHub);
}
return notificationHub;
}
}
`
Is this a good way to do it ? I'm doubting the part where i return the INotificationHub i don't like the fact that i call _serviceProvider.GetService().
If this is not good do you have any suggestions ?