【发布时间】:2015-08-12 10:24:07
【问题描述】:
我目前正在尝试使用 DI/IOC 为我的多模块解决方案找到更好的设计,但现在我不知何故迷路了。我有一个解决方案,可以通过不同的渠道将不同类型的实体分发给收件人。 这是我的课程的简化版本:
#region FTP Module
public interface IFtpService
{
void Upload(FtpAccount account, byte[] data);
}
public class FtpService : IFtpService
{
public void Upload(FtpAccount account, byte[] data)
{
}
}
#endregion
#region Email Module
public interface IEmailService :IDistributionService
{
void Send(IEnumerable<string> recipients, byte[] data);
}
public class EmailService : IEmailService
{
public void Send(IEnumerable<string> recipients, byte[] data)
{
}
}
#endregion
public interface IDistributionService { }
#region GenericDistributionModule
public interface IDistributionChannel
{
void Distribute();
}
public interface IDistribution
{
byte[] Data { get; }
IDistributionChannel DistributionChannel { get; }
void Distribute();
}
#endregion
#region EmailDistributionModule
public class EmailDistributionChannel : IDistributionChannel
{
public void Distribute()
{
// Set some properties
// Call EmailService???
}
public List<string> Recipients { get; set; }
}
#endregion
#region FtpDistributionModule
public class FtpDistributionChannel : IDistributionChannel
{
public void Distribute()
{
// Set some properties
// Call FtpService???
}
public FtpAccount ftpAccount { get; set; }
}
#endregion
#region Program
public class Report
{
public List<ReportDistribution> DistributionList { get; private set; }
public byte[] reportData{get; set; }
}
public class ReportDistribution : IDistribution
{
public Report Report { get; set; }
public byte[] Data { get { return Report.reportData; } }
public IDistributionChannel DistributionChannel { get; private set; }
public void Distribute()
{
DistributionChannel.Distribute();
}
}
class Program
{
static void Main(string[] args)
{
EmailService emailService = new EmailService();
FtpService ftpService = new FtpService();
FtpAccount aAccount;
Report report;
ReportDistribution[] distributions =
{
new ReportDistribution(new EmailDistributionChannel(new List<string>("test@abc.xyz", "foo@bar.xyz"))),
new ReportDistribution(new FtpDistributionChannel(aAccount))
};
report.DistributionList.AddRange(distributions);
foreach (var distribution in distributions)
{
// Old code:
// if (distribution.DistributionChannel is EmailDistributionChannel)
// {
// emailService.Send(...);
// }else if (distribution.DistributionChannel is FtpDistributionChannel)
// {
// ftpService.Upload(...);
// }else{ throw new NotImplementedException();}
// New code:
distribution.Distribute();
}
}
}
#endregion
在我当前的解决方案中,可以创建和存储持久性IDistribution POCO(我在这里使用ReportDistribution)并将它们附加到可分发实体(在本例中为Report)。
例如。有人想通过电子邮件将现有的Report 分发给一组收件人。因此他创建了一个新的ReportDistribution' with anEmailDistributionChannel'。后来他决定通过 FTP 将相同的Report 分发到指定的 FtpServer。因此,他用FtpDistributionChannel 创建了另一个ReportDistribution。
可以在相同或不同的频道上多次分发相同的Report。
Azure Webjob 获取存储的 IDistribution 实例并分发它们。当前,丑陋的实现使用 if-else 通过(低级)FtpService 和 EmailDistributionChannels 和 EmailService 分发 Distributions 和 FtpDistributionChannel。
我现在正在尝试在FtpDistributionChannel 和EmailDistributionChannel 上实现接口方法Distribute()。但要使其正常工作,实体需要对服务的引用。通过 ConstructorInjection 将服务注入实体似乎被认为是不好的风格。
Mike Hadlow 提出了另外三个解决方案:
创建域服务。我可以例如创建一个
FtpDistributionService,注入一个FtpService并编写一个Distribute(FtpDistributionChannel distribution)方法(还有一个EmailDistributionService)。除了 Mike 提到的缺点之外,如何根据IDistribution实例选择匹配的DistributionService?用另一个替换我的旧 if-else 感觉不对将
IFtpService/EMailService注入Distribute()方法。但是我应该如何在IDistribution接口中定义Distribute()方法呢?EmailDistributionChannel需要IEmailService而FtpDistributionChannel需要IFtpService。域事件模式。我不确定这如何解决我的问题。
让我试着解释一下为什么我想出了这个相当复杂的解决方案: 它从一个简单的报告列表开始。很快有人要求我向一些收件人发送报告(并存储收件人列表)。简单的!
后来,其他人添加了将报告发送到 FtpAccount 的要求。在应用程序中管理不同的 FtpAccounts,因此还应存储所选帐户。 这就是我添加 IDistributionChannel 抽象的地步。一切都还好。
然后有人需要通过电子邮件发送某种持久性日志文件的可能性。这导致了我使用 IDistribution/IDistributionChannel 的解决方案。 如果现在有人需要分发其他类型的数据,我可以为这些数据实现另一个 IDistribution。如果需要另一个 DistributionChannel(例如 Fax),我会实施它,它适用于所有可分发实体。
非常感谢任何帮助/想法。
【问题讨论】:
标签: c# oop dependency-injection inversion-of-control