Редизайн с использованием шаблонного метода и параметризованного заводского метода.Инверсия зависимостей - PullRequest
0 голосов
/ 30 июня 2011

Я решил перепроектировать определенную подсистему, используя недавно приобретенное базовое понимание шаблонов проектирования.

Есть две абстракции, которые я вижу, и поэтому они стали интерфейсами. Один следует шаблону шаблонного метода ( 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);
            }
        }

1 Ответ

1 голос
/ 30 июня 2011

Мне действительно трудно прочитать ваш вопрос, сохранить последний абзац и некоторые другие, но я все равно постараюсь ответить.

Из того, что я вижу в коде, вместо использования if операторы для определения типа объекта IAdapterConnectionInfo, вам просто нужно перегрузить метод для каждого типа.Это значительно повысит эффективность и расширяемость - я не знаю, возможно ли это в вашем шаблоне, но если новый класс унаследован от старого, который реализует IAdapterConnectionInfo, и вы проверили на ia is OldClass до ia is DerivedClass, вы потеряетеспецифичность.То же самое относится и к методу CreateParsers.Вызывающий метод может позаботиться о проблемах и использовать RTTI и приведение на свою сторону, если это необходимо, но ваш класс больше не полагается на этот дизайн, что, я думаю, было бы неплохо.

ДругойЧтобы решить эту проблему, можно создать метод в IAdapterConnectionInfo, который возвращает тип правильного связанного ServerSettingsAdapter, и другой метод (может существовать или не существовать) IServerSettingsAdapter, который возвращает соответствующий тип Parser.Затем вы можете использовать отражение и приведение, чтобы загрузить их.Это может или не может работать - я не могу сказать.

Извините, если я не ответил на правильный вопрос ...: P

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...