2016-12-07 1 views
0

Это скорее вопрос дизайна, а более конкретно, мне было интересно, есть ли лучший способ сделать это. В основном я делаю реализацию для калькуляторов, где различные типы отпуска (оплаченные, неоплаченные и т. Д.) Могут быть рассчитаны для разных фирм. Каждая фирма имеет тенденцию иметь свой собственный поворот на них, такой как округление вверх/вниз, больше/меньше дней и т. Д.Как я могу улучшить дизайн этого заводского класса, который имеет множество различных бетонов?

Калькуляторы связаны с разными кодами, которые в свою очередь связаны с конкретными фирмами (например, код A используется только в FirmOne) - не мой дизайн, но я должен уважать их.

Упрощенный пример:

internal class CalculatorFactory 
{ 
    internal ICalculator Create(string calculatorCode) 
    { 
     ICalculator result; 
     switch (calculatorCode) 
     { 
      case FirmOne.CalculatorCodes.A: 
      case FirmTwo.CalculatorCodes.B: 
       result = new FastCalculator(); 
       break; 
      case FirmOne.CalculatorCodes.X: 
      case FirmTwo.CalculatorCodes.Y: 
       result = new SlowCalculator(); 
       break; 
      case FirmThree.CalculatorCodes.Z: 
       result = new VerySlowCalculator(); 
       break; 
      default: 
       throw new NotSupportedException(); 
     } 

     return result; 
    } 
} 

internal interface ICalculator 
{ 
    decimal Calculate(); 
} 

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

Мне особо не нравится код, переданный в виде строки, поэтому первое улучшение может состоять в том, чтобы сделать его перечислением, но кроме этого ..? Возможно сделать отдельные фабрики на одну фирму.?

Заранее благодарен!

+1

Почему вы не спросили об этом в Сообществе Code Review? – Badiparmagi

+1

Это лучше всего подходит для разработки программного обеспечения, а не для Code Review, поскольку это больше касается шаблона, чем кода. – toadflakz

+1

Я голосую, чтобы закрыть этот вопрос как не относящийся к теме, потому что он должен быть перенесен в Software Engineering SE - вопрос касается шаблона, а не кода, поэтому я не предлагаю его перенести в Code Review SE. – toadflakz

ответ

2

По-моему, у вас есть два варианта, оба они не исключают друг друга. Сначала вы можете выбрать Dictionar<string, Type> или альтернативно Dictionary<string, Func<ICalculator>>, который хранит коды и типы или конструкторы для создания экземпляров повторно. Для последнего here's примера:

var types = new Dictionary<string, Func<ICalculator>> 
{ 
    { "A", new Func<ICalculator>(() => new FastCalculator()), 
    { "B", new Func<ICalculator>(() => new FastCalculator()), 
    { "X", new Func<ICalculator>(() => new SlowCalculator()), 
}; 

Теперь вместо того, чтобы полагаться на отражении вы могли бы использовать это, чтобы создавать экземпляры ваших типов:

var newInstance = types["A"](); 

Другой вариант заключается в использовании конфиг-файл в котором вы можете указать все возможные типы. Теперь прочитайте файл и присвойте один тип каждому имени внутри этого файла.

<type code="A" name="MyNamespace.FastCalculator" /> 
<type code="B" name="MyNamespace.FastCalculator" /> 
<type code="X" name="MyNamespace.SlowCalculator" /> 

Теперь вы можете легко создавать экземпляры этих типов с помощью отражения:

var myInstance = Activator.CreateInstance(type.Name) as ICalculator; 

Конечно, вы можете также выбрали Dictionary здесь, чтобы кэшировать типы или даже конструкторы themselfes с использованием Func<ICalculator>.

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

+0

Возможно, я должен был быть более конкретным в моем вопросе, все калькуляторы используются для каждого запуска приложения. В основном это запланированная работа для обработки сотрудников разных фирм. В зависимости от фирмы сотрудника будет использоваться другой набор калькуляторов. Как вы относитесь к использованию словарей в целом ...? Я не могу не почувствовать, что это нечеткая абстракция - это заставляет вызывающего пользователя узнать некоторые подробности о реализации (например, код). – rumblefx0

+0

Чтобы оставаться открытым для изменений, перечисление может быть плохим выбором, так как вам нужно будет изменить перечисление для каждого нового калькулятора. Однако вы также можете выбрать абстрактный завод, посмотрите https://en.wikipedia.org/wiki/Abstract_factory_pattern, где вы получите один завод для каждой фирмы. – HimBromBeere

+0

Это было направление, в которое я двигался, что кажется чище. У вас все еще есть такой большой коммутатор на фабрику, но вы должны принять решение о том, какой тип я предполагаю. – rumblefx0

2

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

Вы можете украсить свои реализации ICalculator этим атрибутом. При запуске приложения или инициализации вашего CalculatorFactory вы можете проверить все свои типы ICalculator и создать словарь, который содержит определенный тип ICalculator, который должен быть создан для конкретного калькулятора.

Затем завод просто ищет тип, который должен использоваться для заданного кода, и создайте экземпляр этого типа.

Что-то вроде этого:

[CalculatorCode("xyz")] 
[CalculatorCode("foo")] 
public class FastCalculator : ICalculator { ... } 


public static class CalculatorFactory 
{ 

    private static Dictionary<string, Type> _calcDictionary = new Dictionary<string, Type>(); 

    static CalculatorFactory() 
    { 
     var calctypes = Assembly.GetExecutingAssembly()GetTypes().Where(t => t.IsAssignableFrom(typeof(ICalculator))).ToList(); 

     foreach(var ct in calctypes) 
     { 
      var attributes = (CalculatorCodeAttribute[])Attribute.GetCustomAttributes (ct, typeof (CalculatorCodeRuleAttribute)); 

      foreach(var attr in attributes) 
      { 
       // add the info to a dictionary 
       _calcDictionary.Add(attr.CalculatorCode, ct); 
      } 
     } 


    } 

    public static ICalculator Create(string calcCode) 
    { 
     if(_calcDictionary.ContainsKey(calcCode) == false) 
     { 
       throw new ArgumentException("No ICalculator defined for the given code"); 
     } 

     return _calcDictionary[calcCode]; 
    } 

} 

There might be syntax errors or imperfections in the code above. It is just to g 

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

Чтобы сделать его более расширяемым, вы также можете взглянуть на Microsoft Extensibility Framework.

+0

Интересная реализация, я получаю ощущение, что это будет распространять все калькуляторы на разные классы, хотя (через атрибуты), чего я хочу избежать. – rumblefx0

+1

Другим способом является отслеживание какого-либо реестра в центральном месте. Вы можете заполнить словарь в классе Factory самостоятельно, например ... –

0

Вы можете использовать различные контейнеры DI. Autofac, Unity, Ninject и т. Д. Вы можете загружать бетон в интерфейсы с помощью кода или конфигурации xml.

Использование каркасов DI (вы можете написать свой собственный) дает вам больше, чем просто заводские методы, это изменяет способ построения вашего кода, чтобы быть более открытым для изменений и расширений.

Смежные вопросы