Вы кладете свои расчеты на свои наборы или ваши доходы. - PullRequest
9 голосов
/ 15 января 2009

что лучше ???

public class Order
{
   private double _price;
   private double _quantity;

  public double TotalCash
  {      
   get
   {
    return _price * _quantity;
   }
}

или

public class Order
{

   private double _totalCash;
   private double _price;
   private double _quantity;

  private void CalcCashTotal()
 {
   _totalCash = _price * _quantity
 }

  public double Price
  {      
   set
   {
     _price = value;
     CalcCashTotal();
   }
  }

  public double Quantity
  {      
   set
   {
     _price = value;
     CalcCashTotal();
   }
  }


  public double TotalCash
  {      
   get
   {
    return _totalCash;
   }
}

Ответы [ 13 ]

14 голосов
/ 15 января 2009

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

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

  public class Order
  {
     private double _totalCash;
     private double _price;
     private double _quantity;
     private _IsDirtyTotalCash = true;

  private void CalcCashTotal()
  {
    _totalCash = _price * _quantity
  }

  public double Price
  {      
   set
   {
     _price = value;
     _IsDirtyTotalCash = true;
   }
  }

  public double Quantity
  {      
   set
   {
     _price = value;
     _IsDirtyTotalCash = true;
   }
  }

  public double TotalCash
  {      
   get
   {
        if(_IsDirtyTotalCash)
    {
      _totalCash = CalcTotalCost();
       _isDirtyTotalCash = false;
     }
     return _totalCash;
   }
  }

}
4 голосов
/ 15 января 2009

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

В вашем примере цена / количество у вас может быть один отдельный метод, который пересчитывает количество при установке цены или количества.

3 голосов
/ 15 января 2009

При определении того, следует ли выводить / вычислять свойство, важно учитывать, следует ли сохранить значение на момент расчета.

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

просто выкладываю это туда ...

3 голосов
/ 15 января 2009

Я бы согласился с гибридным предложением Чарльза Грэма, но я хочу добавить два своих цента, почему.

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

double subTotal = myOrder.TotalCash;
double tax = subTotal * 0.05;
double shipping = subTotal > 100 ? 0 : 5.95;
double grandTotal = subTotal + tax + shipping;
OutputToUser(subTotal, tax, shipping, grandTotal);

Другие люди не могут. Видя, что myOrder.TotalCash это свойство, а не метод, по крайней мере, я бы предположил, что это кэшированное значение. То есть доступ к subTotal в приведенном выше примере сопоставим по эффективности с доступом к myOrder.TotalCash. Не понимая, что за кулисами происходит расчет, они пишут:

double tax = myOrder.TotalCash * 0.05;
double shipping = myOrder.TotalCash > 100 ? 0 : 5.95;
double grandTotal = myOrder.TotalCash + tax + shipping;
OutputToUser(myOrder.TotalCash, tax, shipping, grandTotal);

оставив myOrder.TotalCash для обозначения промежуточного итога. Теперь он был рассчитан 4 раза вместо 1.

Таким образом, я уверен, что я не единственный, кто считает, что свойство представляет переменную или кэшированное значение и метод обрабатывает что-то и возвращает значение . Имеет смысл хранить CalculateTotalCash() и вызывать его только один раз, потому что вы ожидаете, что это будет падение производительности. С другой стороны, вы ожидаете, что TotalCash будет кэшированным значением и сможете получить к нему доступ по своему усмотрению. Поэтому важно только пересчитать TotalCash, когда он изменится.

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

3 голосов
/ 15 января 2009

Первый лучше, потому что:

  • Это короче.
  • Это легче понять.
  • Немного самонадеянно пересчитывать TotalCash каждый раз, когда устанавливается цена или количество. Он должен быть как можно более ленивым и вычислять только по запросу.

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

2 голосов
/ 15 января 2009

Первый, потому что:
1) Чем меньше кода, тем лучше;
2) Меньше сложности;
3) Меньше переменных помогает получить меньше коллатеральных проблем;
4) свойство будет всегда обновляться;
5) Если вы измените имя процедуры «CalcCashTotal», вы получите больше других пунктов для изменения ...

1 голос
/ 24 августа 2009

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

1 голос
/ 15 января 2009

Первый вариант лучше. Разница в «оптимизации» ничтожна. Не говоря уже о том, что если настройка происходит снова и снова, но вам нужно всего лишь один раз получить TotalCost? Я бы больше беспокоился о потерянных часах разработчиков, пытающихся отладить класс, когда он станет очень сложным.

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

class Wall {
    public decimal Width {
       get {
           ...
       }
       set {
           ValidateChanges();
           width = value;
           CreateBays();
       }
    }

    public Bays[] Bays {
       get {
           ...
       }
       set {
           ValidateChanges();
           ...
       }
    }

    private void CreateBays() {
        // Delete all the old bays.
        ...
        // Create a new bay per spacing interval given the wall width.
        ...
    }
}

Здесь каждый раз, когда ширина меняется, перемычки в стене воссоздаются. Если бы это произошло на Bay.getter, это было бы довольно пагубно для свойств объекта Bay. Получатель должен был бы выяснить, изменилась ли ширина или нет с момента последнего оператора get, увеличивая сложность .

1 голос
/ 15 января 2009

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

Это мои 2 цента ... но даже я бы, наверное, просто пошел с вашим первым примером, если класс действительно такой простой: -)

1 голос
/ 15 января 2009

Помещение в функцию get не очень идеально. Вы будете пересчитывать это без причины. Это даже не имеет никакого смысла. Так что здесь есть случай, когда :: gasp :: оптимизация имеет смысл и является предпочтительной. Рассчитайте его один раз и пожинайте плоды.

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