【发布时间】:2021-08-01 18:43:04
【问题描述】:
我在 ASP.NET Core 应用程序中使用库 MediatR。
我有以下实体Ad:
public class Ad
{
public Guid AdId { get; set; }
public AdType AdType { get; set; }
public double Cost { get; set; }
public string Content { get; set; }
// ...
}
public enum AdType
{
TextAd,
HtmlAd,
BannerAd,
VideoAd
}
我想介绍制作新广告的功能。为此,我创建了以下命令:
public class CreateAdCommand : IRequest<Guid>
{
public AdType AdType { get; set; }
public double Cost { get; set; }
public string Content { get; set; }
public class Handler : IRequestHandler<CreateAdCommand, Guid>
{
private readonly MyDbContext _context;
public Handler(MyDbContext context)
{
_context = context;
}
public async Task<Guid> Handle(CreateAdCommand request, CancellationToken cancellationToken)
{
var ad = new Ad {AdType = request.AdType, Cost = request.Cost, Content = request.Content};
_context.Ads.Add(ad);
_context.SaveChangesAsync();
return ad.AdId;
}
}
}
这段代码很好用。但是这里有一个大问题:每种广告类型在广告创建过程中都有一些额外的逻辑(例如,在创建TextAd 类型的广告时,我们需要在广告内容中找到关键字)。最简单的解决方案是:
public async Task<Guid> Handle(CreateAdCommand request, CancellationToken cancellationToken)
{
var ad = new Ad {AdType = request.AdType, Cost = request.Cost, Content = request.Content};
_context.Ads.Add(ad);
_context.SaveChangesAsync();
switch (request.AdType)
{
case AdType.TextAd:
// Some additional logic here...
break;
case AdType.HtmlAd:
// Some additional logic here...
break;
case AdType.BannerAd:
// Some additional logic here...
break;
case AdType.VideoAd:
// Some additional logic here...
break;
}
return ad.AdId;
}
这种解决方案违反了开放封闭原则(当我创建一个新的广告类型时,我需要在CreateAdCommand 内创建一个新的case)。
我有另一个想法。我可以为每种广告类型创建一个单独的命令(例如,CreateTextAdCommand、CreateHtmlAdCommand、CreateBannerAdCommand、CreateVideoAdCommand)。此解决方案遵循开放封闭原则(当我创建一个新的广告类型时,我需要为这个广告类型创建一个新命令 - 我不需要更改现有代码)。
public class CreateTextAdCommand : IRequest<Guid>
{
public double Cost { get; set; }
public string Content { get; set; }
public class Handler : IRequestHandler<CreateTextAdCommand, Guid>
{
private readonly MyDbContext _context;
public Handler(MyDbContext context)
{
_context = context;
}
public async Task<Guid> Handle(CreateTextAdCommand request, CancellationToken cancellationToken)
{
var ad = new Ad {AdType = AdType.TextAd, Cost = request.Cost, Content = request.Content};
_context.Ads.Add(ad);
await _context.SaveChangesAsync();
// Some additional logic here ...
return ad.AdId;
}
}
}
public class CreateHtmlAdCommand : IRequest<Guid>
{
public double Cost { get; set; }
public string Content { get; set; }
public class Handler : IRequestHandler<CreateHtmlAdCommand, Guid>
{
private readonly MyDbContext _context;
public Handler(MyDbContext context)
{
_context = context;
}
public async Task<Guid> Handle(CreateHtmlAdCommand request, CancellationToken cancellationToken)
{
var ad = new Ad {AdType = AdType.HtmlAd, Cost = request.Cost, Content = request.Content};
_context.Ads.Add(ad);
await _context.SaveChangesAsync();
// Some additional logic here ...
return ad.AdId;
}
}
}
// The same for CreateBannerAdCommand and CreateVideoAdCommand.
该方案遵循开闭原则,但违反了 DRY 原则。我该如何解决这个问题?
【问题讨论】:
-
我已经用一种跨管道共享数据的机制更新了我的答案。