#architecture
#архитектура
Вопрос:
У меня есть класс под названием MessageService. Этот класс отвечает за получение электронной почты (фактически потоки). В зависимости от темы письма он определяет, о каком потоке идет речь, и выполняет действие.
немного кода для объяснения моей проблемы может быть более простым :
public class MessageService
{
public void ReadEmail()
{
switch (subject)
"1" :
Myservice.action1(); break;
"2" :
Myservice.action2(); break;
"qwwerty" :
MyOtherservice.Querty(); break;
etc...
}
}
Чтобы выполнить некоторую инверсию управления, я хотел передать ссылки на сервисы через конструктор моего класса MessageService.
public MessageService(IMyService myService,IMyOtherservice myOtherservice, ect....)
{
Myservice=myService;
MyOtherservice=myOtherservice;
}
Это было бы отлично для нескольких ссылок, но класс MessageService может обрабатывать до 20,30 или сорока различных потоков. И это сделало бы инициализацию моего класса немного тяжелой.
Есть ли какой-нибудь более приятный способ добиться этого? через какой-либо шаблон проектирования? Должен ли я заботиться о IOC (хотя мне нравится запускать мои тесты в классе после ..)?
Спасибо за вашу помощь,
Ответ №1:
Вероятно, вы захотите использовать шаблон проектирования цепочки ответственности. Создайте интерфейс, который знает, как работать с электронными письмами, и создайте отдельные классы для каждой задачи, которую вы хотите выполнить (примерно эквивалентно вашему оператору switch ( switch
, кстати, это очень наивная реализация COR)). Каждый из этих классов будет использовать только тот сервис, который им требуется. Затем в MessageService
возьмите список этих интерфейсов и обрабатывайте их по одному, пока один из них не укажет, что он обработал это:
public interface IMessageReader
{
/// <returns><c>true</c> if handled; otherwise false
bool ReadEmail(Mail message);
}
public class QwertyMessageReader : IMessageReader
{
public QwertyMessageReader(IMyOtherService otherService) {/*set*/}
public bool ReadEmail(Mail message)
{
if(message.Subject.Equals("qwwerty"))
{
otherService.Querty();
return true;
}
return false;
}
}
public class MessageService
{
public MessageService(IEnumerable<IMessageReader> readers) {/*set*/}
public void ReadEmail()
{
var handled = readers.Select(reader => reader.ReadEmail(message))
.FirstOrDefault(result => result);
}
}
Вам нужно будет настроить интерфейсы и приведенный выше код в соответствии с тем, что вы делаете, но это основная концепция.
Большинство приличных контейнеров IOC автоматически сгенерируют для вас IEnumerable<>, если вы зарегистрируете несколько одного и того же интерфейса.
Комментарии:
1. Хорошее решение, мне оно очень нравится. К сожалению, я не уверен, что мой IOC справится с этой задачей, потому что он несколько самодельный, созданный каким-то коллегой, и люди действительно неохотно обращаются к чему-то другому. Я посмотрю, что я могу сделать, и вернусь.. Спасибо вам!
Ответ №2:
Лучшим предложением было бы разбить ваш класс на более мелкие классы. Наличие 30 или 40 зависимостей в одном классе является признаком того, что он выполняет слишком много. Попробуйте разбить каждый класс на что-то, что выполняет только одну вещь, а затем вы можете начать объединять их в более крупные части функциональности. Поскольку вы используете контейнер IoC, это на самом деле не должно быть слишком сложно сделать, и ваш код будет легче тестировать. Идеальной целью было бы использовать одну или две зависимости для каждого класса, но реально, я думаю, что 4 или 5 — хорошая цель для начала.
Не зная точно, что делает ваш код, трудно предложить конкретные шаблоны проектирования.
Комментарии:
1. Я тоже так думал, но я немного растерялся, не зная, как разбить мои классы… Я дам более глубокий обзор и сообщу вам, если что-нибудь найду.. Все равно спасибо.