В настоящее время я пытаюсь найти лучший дизайн для моего многомодульного решения с использованием DI/IOC, но теперь я как-то потерялся. У меня есть решение, где различные типы сущностей могут быть распределены получателям по разным каналам. Это упрощенная версия моих классов:Улучшение дизайна с помощью IOC/DI
#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>("[email protected]", "[email protected]"))),
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
Pocos (i'am использование ReportDistribution
здесь) и прикрепить их к распределяемой сущности (а Report
в этом примере). . кто-то хочет распространить существующий Report
по электронной почте на набор получателей. Поэтому он создает новый ReportDistribution' with an
EmailDistributionChannel '. Позже он решает распространять те же Report
через FTP на указанный FtpServer. Поэтому он создает еще ReportDistribution
с FtpDistributionChannel
. Можно распространять те же самые Report
несколько раз на одном и том же или разных каналах.
Azure Webjob забирает хранящиеся IDistribution
экземпляры и распространяет их. Нынешняя, уродливая реализация использует if-else для распределения Distributions
с FtpDistributionChannel
через (низкоуровневые) FtpService
и EmailDistributionChannels
с EmailService
.
Теперь я пытаюсь реализовать метод интерфейса Distribute()
на FtpDistributionChannel
и EmailDistributionChannel
. Но для этого нужно, чтобы организации нуждались в ссылке на службы. Внедрение служб в объекты через ConstructorInjection представляется плохой.
Mike Hadlow поставляется с тремя другими решениями:
Создание доменных служб. Я мог бы, например, создайте
FtpDistributionService
, введитеFtpService
и напишитеDistribute(FtpDistributionChannel distribution)
(а такжеEmailDistributionService
). Помимо недостатка, упомянутого Майком, как я могу выбрать подходящий номерDistributionService
на основе экземпляраIDistribution
? Замена моего старого if-else другим не кажется правильнымInject
IFtpService/EMailService
в методDistribute()
. Но как я должен определить методDistribute()
в интерфейсеIDistribution
?EmailDistributionChannel
нуждается вIEmailService
, в то время какFtpDistributionChannel
нуженIFtpService
.Структура событий домена. Я не уверен, как это может решить мою проблему.
Позвольте мне объяснить, почему я пришел с этим довольно сложным решением: Это началось с простого списка отчетов. Вскоре кто-то попросил меня отправлять отчеты некоторым получателям (и хранить список получателей). Легко!
Позже кто-то добавил требование отправить отчет в FtpAccount. В приложении управляются разные FtpAccounts, поэтому выбранная учетная запись также должна быть сохранена. Это было до такой степени, что я добавил абстракцию IDistributionChannel. Все было хорошо.
Затем кому-то была нужна возможность также отправлять какие-то постоянные лог-файлы по электронной почте. Это привело к моему решению с помощью модуля распределения/распределения/распределения. Если теперь кому-то нужно распространять некоторые другие данные, я могу просто реализовать другое распределение для этих данных. Если требуется другой канал DistributionChannel (например, факс), я реализую его и доступен для всех распространяемых объектов.
Я бы очень признателен за любую помощь/идеи.
Спасибо, jgauffin! Я думаю, что мой первоначальный образец кода был слишком упрощен. Я изменил его, чтобы быть ближе к моей реальной реализации и добавил некоторые дополнительные комментарии. Ваш код выглядит интересным, но я не уверен, соответствует ли он моим требованиям. Может быть, вы можете еще раз взглянуть на мой отредактированный вопрос. – krombi
@krombi: прочитайте мое обновление – jgauffin
Теперь я чувствую себя глупо, но я думаю, что до сих пор не понимаю вашего предлагаемого решения. My Distribution/EmailDistribution/FtpDistribution-Stuff разрабатывается в отдельных проектах/пакетах nuget. Поэтому добавить функциональность Distribution в другое приложение довольно просто. Я не пытаюсь скрыть сложность, но хочу создать многоразовые модули. Что вы предлагаете? Создайте абстрактный класс AbstractReportDistribution с унаследованными классами ReportEmailDistribution, ReportFtpDistribution, ReportFaxDistribution, а также 'AbstractLogDistribution' и decendants? – krombi