Расчет изменения номиналов 3


Я пытаюсь рефакторинг большое приложение, чтобы использовать более ДДД принципы, как и когда я получаю шанс. Я концентрируюсь на самых простых первых местах. Приложение в настоящее время имеет очень простое обслуживание домена (один из многих), как он стоит (см. Вариант 1). Пожалуйста, см. ниже код:

Вариант 1 - Жить

public IEnumerable<System.Collections.Generic.KeyValuePair<decimal, int>> CalculateDenominations(decimal cost, ICurrency currency)
        {
            var target = cost;
            foreach (var denomination in currency.AvailableDenominations)
            {
                var numberRequired = target / denomination;
                if (numberRequired >= 1)
                {
                    int quantity = (int)Math.Floor(numberRequired);
                    yield return new KeyValuePair<decimal, int>(denomination, quantity);
                    target = target - (quantity * denomination);
                }
            }
        }

Вариант 2

public sealed class DenominationsRequired
    {
        private readonly decimal _cost;
        private readonly ICurrency _currency;

        public DenominationsRequired(decimal cost, ICurrency currency)
        {
            if (currency == null)
                throw new ArgumentNullException("Currency cannot be null", "ICurrency");
            if (cost < 0)
                throw new ArgumentException("Cost cannot be less than zero", "Cost");
            if (decimal.Round(cost, 2) != cost)
                throw new ArgumentException(string.Concat("Cost has too many decimal places.  It should only have: ", currency.DecimalPlaces), "Cost");
            _cost = cost;
            _currency = currency;
        }

        public decimal Cost
        {
            get { return _cost; }
        }

        public ICurrency Currency
        {
            get { return _currency; }
        }

        public IEnumerable<System.Collections.Generic.KeyValuePair<decimal, int>> CalculateDenominations()
        {
            var target = _cost;
            foreach (var denomination in _currency.AvailableDenominations)
            {
                var numberRequired = target / denomination;
                if (numberRequired >= 1)
                {
                    int quantity = (int)Math.Floor(numberRequired);
                    yield return new KeyValuePair<decimal, int>(denomination, quantity);
                    target = target - (quantity * denomination);
                }
            }
        }
    }

Я думаю о рефакторинге службу домен (Вариант 1 - жить) в объект-значение (вариант 2), так что я могу добавить проверку. Буду благодарен за критические замечания об этом подходе. Это объекта value этого (Вариант 2) "лучше", чем обслуживание домена (Вариант 1) для этого? Я считаю, что это приемлемо, чтобы положить caclculations в объекты значения, поскольку они не мутируют государства.

Я конкретно интересно знать:

  1. Это объект-значение "лучше", чем обслуживание домена для этого конкретные требования?

  2. Разумно ли иметь свойство: Currency.DecimalPlaces (указывает сколько знаков после запятой в валюте, т. е. на 2 кг и 2 Для долларов - некоторым валютам позволит 3 или 4 знака после запятой), который используется в DenominationsRequired конструктор для проверки?

  3. Я должен держать Вариант 1 или Вариант 2? Если Вариант 1, тогда как подход к проверке?



Комментарии
1 ответ


1) объект значение "лучше", чем обслуживание домена?

Это, конечно, субъективно, но я предложу этот прагматической точки зрения. Оценить, насколько часто этот код будет активно ссылаться и отлажены другими разработчиками. Если этот код находится в легко многоразовые коммунальные ассамблеи, и это реалистичное ожидание, что он будет использоваться повторно, то стоимость объекта выигрывает для меня каждый раз. Он предлагает больше документации, чем ваши первоначальные попытки что важно, потому что вы не выполнили комментариях кода.
В конце дня у вас есть, чтобы оправдать время и усилия, требуемые, чтобы получить код производства, код, который используется повторно, особенно другими разработчиками стоит усилий, чтобы получить это право, но вы выросли Вы код поверхности и придется увеличить время модульного тестирования покрытие, чтобы соответствовать.
Стоимость объектов, как это я считаю намного проще для модульного тестирования, и, как вы сказали, с проверкой, объект теперь имеет дополнительные опции для выражения этого состояния не блокируя выполнение исключения и мы можем более легко наследуют эту структуру и, когда нужно переопределить конкретные функции, такие как проверка, когда мы, как разработчики, сделайте повторное использование кода.


2) разумно ли имеют свойство валюты.DecimalPlaces

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


3) Если я иду с вариантом 2?

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

Конечно следовать советам от @t3chb0t, вы ушли к этому особых усилий, реализовать свои собственные договора определение для ответа от CalculateDenominations, значение ключа пары ответ будет слишком неоднозначным для пользователей знают, как использовать ответ, это недостающий кусочек "я" документации.


Как я подход проверки с вариантом 1?

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


Обновление: перемещение проверку явный метод называется метод validate() в дальнейшем позволяет самостоятельно документировать свой класс, и делает его легче поддерживать в долгосрочной перспективе. Ты все еще называешь проверки от метода расчета или конструктор. В качестве бонуса, когда проверка завершается неудачно, стек трассировки покажет DenominationsRequired.Метод validate (), который далее описывает, почему исключение для будущих отладчики.

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


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

Быстрое примечание о вашей проверки, вы жестко 2 в качестве сравнения, а не валюты.DecimalPlaces, так и должно быть:

if (decimal.Round(cost, currency.DecimalPlaces) != cost)
throw new ArgumentException(string.Concat("Cost has too many decimal places. It should only have: ", currency.DecimalPlaces), "Cost");

2
ответ дан 13 февраля 2018 в 09:02 Источник Поделиться