I would be grateful for feedback on the class below. It is used to validate the cost and payment received:
public class Money { private readonly decimal _cost; private readonly decimal _payment; private readonly ICurrency _currency; public Money(decimal cost, decimal payment, ICurrency currency) { if(cost<0) throw new ArgumentException("Cost cannot be less than zero", "Cost"); if (payment < 0) throw new ArgumentException("Payment cannot be less than zero", "payment"); if (payment < cost) throw new ArgumentException("Cost cannot be less than Payment Received"); if (decimal.Round(cost, 2) != cost) throw new ArgumentException(string.Concat("Cost has too many decimal places. It should only have: ", currency.DecimalPlaces),"Cost"); if (decimal.Round(payment, 2) != payment) throw new ArgumentException(string.Concat("payment has too many decimal places. It should only have: ", currency.DecimalPlaces), "payment"); if(currency ==null) throw new ArgumentNullException("Currency cannot be null", "ICurrency"); _cost = cost; _payment = payment; _currency = currency; } public decimal Cost { get { return _cost; } } public decimal payment { get { return _payment; } } public ICurrency Currency { get { return _currency; } } public static bool operator ==(Money money1, Money money2) { if (!ReferenceEquals(money1, null) && ReferenceEquals(money2, null)) { return false; } if (ReferenceEquals(money1, null) && !ReferenceEquals(money2, null)) { return false; } if (ReferenceEquals(money1, null) && ReferenceEquals(money2, null)) { return true; } return money1.Equals(money2); } public static bool operator !=(Money money1, Money money2) { return !(money1 == money2); } public bool Equals(Money other) { if (ReferenceEquals(other, null)) return false; return _cost == other._cost && _payment == other._payment && _currency == other._currency; } public override bool Equals(object obj) { return Equals(obj as Money); } public override int GetHashCode() { unchecked { int hash = 17; hash = hash * 23 + _cost.GetHashCode(); hash = hash * 23 + _payment.GetHashCode(); hash = hash * 23 + _currency.GetHashCode(); return hash; } } }
The class is used by a Domain Service, which calculates the change required to meet a cost. Instead of doing this in the domain service:
public decimal CalculateChange(decimal cost, decimal payment, ICurrency currency) { return payment-cost; }
I can do this (with the code above):
public decimal CalculateChange(Money money) { return money.payment-money.cost; }
With the second approach; I can be sure that the cost and payment received values are validated. However, this seems like an enormous overkill for such a simple program. I would be grateful for any suggestions e.g. is there a good way the Domain Service can do the validation? Plus I would be grateful for feedback about the class in general.
I am trying to use the principle of least astonishment and Domain Driven Design. Therefore I would be grateful for answers in that context.