Я решил перепроектировать определенную подсистему, используя недавно приобретенное базовое понимание шаблонов проектирования.
Есть две абстракции, которые я вижу, и поэтому они стали интерфейсами. Один следует шаблону шаблонного метода ( IAnalyzer ), потому что он имеет общий алгоритм для различных сценариев (отличаются только основные операции) и помещает данные, предоставленные второй абстракцией (анализатором), в базу данных. Другая абстракция, IParser , подключается к некоторому внешнему источнику данных и получает от него данные.
Я также добавил в проект параметризованный класс Factory Method , который может создать экземпляр IParser и экземпляр IAnalyzer. IAnalyzer содержит ссылку на IParser. (Мне кажется, Стратегия , IAnalyzer служит контекстом, а IParser - стратегией, но поправьте меня, если я ошибаюсь.)
Для каждой возможной реализации IParser существует некоторый settings объект, который содержит информацию о соединении. Теперь, согласно литературе, мы можем передать параметр в шаблон фабрики, чтобы создать экземпляр объекта. Поэтому я думаю об использовании этих объектов настроек в качестве параметра для фабричного метода, который создает IParser. На следующем шаге я использую другой фабричный метод для создания объекта IAnalyzer, используя экземпляр IParser, созданный ранее в качестве параметра.
(Объекты настроек хранятся в виде сериализованной формы на диске и не наследуют ни от чего на данный момент. Может быть, они должны?) В основном я использую if-else if логику в фабричных методах, проверяющих тип переданного Объект для создания парсеров. Всякий раз, когда необходимо создать новый тип синтаксического анализатора, необходимо также создать соответствующий объект настроек. Таким образом, добавление нового else if к заводскому методу позаботится о создании нового парсера. (На самом деле существует отношение «один ко многим» между двумя объектами настроек и анализатором, хотя информация о соединении b / c может содержать более одного адреса для подключения к ожидаемым идентичным источникам данных. Поэтому фабричный метод, вероятно, вернет массив анализаторов.)
Будет ли генерация новых классов на основе параметров объекта параметров нарушать Инверсия зависимости принцип ( Абстракции не должны зависеть от деталей, вместо этого детали должны зависеть от абстракций ) как думаешь?
Вот код.
public class DSParsersFactory
{
public static IParser[] CreateParsers(object ConnectionInfo)
{
if (ConnectionInfo == null) throw new ArgumentNullException();
List<IParser> retVal = new List<IParser>();
if (ConnectionInfo is MDSErverSettings)
{
//foreach address
MDSServerSettingsAdapter sa = new MDSServerSettingsAdapter(); //realizes IAdapterConnectionInfo
//init sa with current address
IParser p = CreateParser(sa);
if (p != null) retVal.Add(p);
}
else if (...)
return retVal.ToArray();
}
static IParser CreateParser(IAdapterConnectionInfo ia)
{
if (ia is MDSServerSettingsAdapter)
return new MDSParser(ia);
else if(...)
return null;
}
public static IAnalyzer CreateAnalyzer(IParser p)
{
if (p == null) throw new ArgumentNullException();
if (p is MDSParser) return new MDSAnalyzer(p);
else if (...)
return null;
}
}
Что ты думаешь? Обратите внимание, что для извлечения нескольких адресов из объекта информации о соединении я создал соответствующие классы адаптера, которые будут содержать одну информацию о соединении для передачи ее конструктору IParser. Это утомительно, но я не придумал ничего лучшего. IAdapterConnectionInfo действительно пустой интерфейс прямо сейчас. Мне также не нравится идея передачи типа System.Object в CreateParsers по какой-то причине.
Спасибо за ваше терпение.
P.S. Основной цикл будет выглядеть так:
var settings = Deserialize(path);
IParser [] pArr = DSParsersFactory.CreateParsers(settings);
foreach (IParser prs in pArr)
{
IAnalyzer algorithm = DSParsersFactory.CreateAnalyzer(prs);
if (algorithm != null)
{
int nSuccesses = algorithm.ProcessData();
string msg=string.Format("{0} records out of {1} were processed succussfully", nSuccesses, prs.GetDataCount());
EventLog.WriteEntry(Settings.Default.AppName, msg);
}
}