C #: Базовый класс Абстрактной Стратегии, служащий Абстрактной Фабрикой для объектов Стратегии - PullRequest
3 голосов
/ 05 января 2010

Я пытаюсь создать веб-инструмент для моей компании, который, по сути, использует географический ввод для получения табличных результатов. В настоящее время мой инструмент используют три различных сферы бизнеса и получают три разных вида продукции. К счастью, все выходы основаны на одной и той же идее Master Table - Child Table, и они даже имеют общую Master Table.

К сожалению, в каждом случае связанные строки дочерней таблицы содержат совершенно разные данные. Поскольку это единственный спорный вопрос, я выделил метод FetchChildData в отдельный класс с именем DetailFinder. В результате мой код выглядит так:

DetailFinder DetailHandler;
if (ReportType == "Planning")
  DetailHandler = new PlanningFinder();
else if (ReportType == "Operations")
  DetailHandler = new OperationsFinder();
else if (ReportType == "Maintenance")
  DetailHandler = new MaintenanceFinder();
DataTable ChildTable = DetailHandler.FetchChildData(Master);

Где PlanningFinder, OperationsFinder и MaintenanceFinder являются подклассами DetailFinder.

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

DetailFinder DetailHandler = DetailFinder.Parse(ReportType);

Тем не менее, я в недоумении относительно того, как DetailFinder знает, какой подкласс обрабатывает каждую строку или даже какие подклассы существуют, не просто перенося блок if в метод Parse. Есть ли способ для подклассов зарегистрировать себя с абстрактным DetailFinder?

Ответы [ 6 ]

3 голосов
/ 05 января 2010

Вы можете использовать контейнер IoC, многие из которых позволяют регистрировать несколько служб с разными именами или политиками.

Например, с помощью гипотетического контейнера IoC вы можете сделать это:

IoC.Register<DetailHandler, PlanningFinder>("Planning");
IoC.Register<DetailHandler, OperationsFinder>("Operations");
...

и затем:

DetailHandler handler = IoC.Resolve<DetailHandler>("Planning");

некоторые вариации на эту тему.

Вы можете посмотреть на следующие реализации IoC:

1 голос
/ 05 января 2010

Возможно, вы захотите использовать карту типов для методов создания:

public class  DetailFinder
{
    private static Dictionary<string,Func<DetailFinder>> Creators;

    static DetailFinder()
    {
         Creators = new Dictionary<string,Func<DetailFinder>>();
         Creators.Add( "Planning", CreatePlanningFinder );
         Creators.Add( "Operations", CreateOperationsFinder );
         ...
    }

    public static DetailFinder Create( string type )
    {
         return Creators[type].Invoke();
    }

    private static DetailFinder CreatePlanningFinder()
    {
        return new PlanningFinder();
    }

    private static DetailFinder CreateOperationsFinder()
    {
        return new OperationsFinder();
    }

    ...

}

Используется как:

DetailFinder detailHandler = DetailFinder.Create( ReportType );

Я не уверен, что это намного лучше, чем ваше утверждение if, но оно делает его как легко читать, так и расширять. Просто добавьте метод создания и запись на карте Creators.

Другой альтернативой может быть сохранение карты типов отчетов и типов поиска, а затем использование Activator.CreateInstance для типа, если вы всегда просто вызываете конструктор. Приведенная выше информация о фабричном методе, вероятно, была бы более уместной, если бы создание объекта было более сложным.

public class DetailFinder
{
      private static Dictionary<string,Type> Creators;

      static DetailFinder()
      {
           Creators = new Dictionary<string,Type>();
           Creators.Add( "Planning", typeof(PlanningFinder) );
           ...
      }

      public static DetailFinder Create( string type )
      {
           Type t = Creators[type];
           return Activator.CreateInstance(t) as DetailFinder;
      }
}
0 голосов
/ 05 января 2010

Как сказал Марк, большой блок if / switch не так уж и плох, поскольку он все будет в одном месте (вся компьютерная наука в основном сводится к получению подобия в некотором пространстве).

Тем не менее, я бы, вероятно, просто использовал полиморфизм (таким образом, чтобы система типов работала для меня). Пусть в каждом отчете будет реализован метод FindDetails (я бы унаследовал их от абстрактного класса Report), так как в любом случае вам придется заканчивать несколько видов поиска деталей. Это также имитирует сопоставление с образцом и алгебраические типы данных из функциональных языков.

0 голосов
/ 05 января 2010

Вы можете использовать отражение. Существует пример кода для метода Parse DetailFinder (не забудьте добавить проверку ошибок в этот код):

public DetailFinder Parse(ReportType reportType)
{
    string detailFinderClassName = GetDetailFinderClassNameByReportType(reportType);
    return Activator.CreateInstance(Type.GetType(detailFinderClassName)) as DetailFinder;
}

Метод GetDetailFinderClassNameByReportType может получить имя класса из базы данных, файла конфигурации и т. Д.

Я думаю, что информация о шаблоне "Плагин" будет полезна в вашем случае: P EAA: Плагин

0 голосов
/ 05 января 2010

Чтобы избежать постоянно растущего блока if..else, вы можете переключить его, чтобы индивидуальные искатели регистрировали, какой тип они обрабатывают с помощью фабричного класса.

Фабричный класс при инициализации должен будет обнаружить все возможные искатели и сохранить их в хэш-карте (словаре). Это может быть сделано путем отражения и / или использования структуры управляемого расширения, как предлагает Марк Симанн.

Однако - будьте осторожны, делая это слишком сложным. Предпочитаю делать простейшие вещи, которые могли бы сработать сейчас, с целью реструктуризации, когда вам это нужно. Не создавайте сложную самонастраивающуюся инфраструктуру, если вам когда-нибудь понадобится только еще один тип поиска;)

0 голосов
/ 05 января 2010

Пока большой блок if или оператор switch или что бы то ни было, появляется только в одном месте, это не плохо для удобства обслуживания, поэтому не беспокойтесь об этом по этой причине.

Однако, когда дело доходит до расширяемости, все обстоит иначе. Если вы действительно хотите, чтобы новые детекторы детекторов могли регистрироваться самостоятельно, вы можете взглянуть на Managed Extensibility Framework , который, по сути, позволяет переносить новые сборки в папку «надстройки» или подобное и основное приложение затем автоматически подберет новые детекторы детализации.

Однако я не уверен, что это именно тот объем расширяемости, который вам действительно нужен.

...