【问题标题】:do you put your calculations on your sets or your gets .你把你的计算放在你的集合上还是你的得到。
【发布时间】:2009-01-15 04:55:51
【问题描述】:

哪个更好???

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;
   }
}

【问题讨论】:

  • 为您重新标记,几乎任何面向对象的语言都允许此决定,并且您将获得更多没有特定语言标签的回复,因为您的示例是 C#
  • 取决于您的具体情况和(非)功能要求... ;-)

标签: c# language-agnostic class methods oop


【解决方案1】:

需要权衡取舍。如果计算简单并且不需要很长时间,那么就把它放在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;
   }
  }

}

【讨论】:

  • 如果计算成本很高,我通常会采用这种模式。
  • 如果抛出异常,_isDirtyTotalCash = false 应该在 CalcTotalCost 之后。说...除以零等。
  • Allain:很好,我刚刚编辑了帖子以反映您的更改。
【解决方案2】:

通常我会尝试将它们放在场景中,因为它们生成的值将存储在内部并且只需要计算一次。只有在每次查询时值都可能发生变化时,您才应该在 get 上进行计算。

在您的价格/数量示例中,您实际上可以有一个单独的方法,在设置价格或数量时重新计算数量。

【讨论】:

  • 对不起,错过了,因为缩进我认为该方法是一个类。
【解决方案3】:

第一个更好,因为:

  • 它更短。
  • 更容易理解。
  • 每次设置价格或数量时重新计算 TotalCash 有点冒昧。它应该尽可能地懒惰,并且只在请求时计算。

话虽如此,将计算放入 setter 可以有效地缓存它,因此如果您遇到性能问题,这可能是一个有用的更改(以清晰度为代价)。

【讨论】:

  • 太懒了。这与延迟加载不同。通过这样做,您实际上会大大减少懒惰,因为您将重新计算更多;一般来说,变量的读取频率高于写入频率。
  • 在这种情况下,我对惰性的定义是 TotalCash 仅按需计算,而不是每次更改它的“上游”组件之一。
【解决方案4】:

我会接受 Charles Graham 的混合建议,但我想补充两分钱来说明原因。

上面的很多建议都谈到了复杂性和优化,但是当你考虑到你的类的消费者时,忘了这一切都消失了。如果是唯一的消费者,并且你使用了第一个实现,你很可能会记得:

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 更改时重新计算它。

混合建议在读取之间有多个集合的情况下获胜。这样您就不会浪费时间计算要丢弃的值。

【讨论】:

    【解决方案5】:

    在确定是否应派生/计算属性时,重要的是要考虑计算时的值是否需要持久化。

    在 TotalCash 的这种情况下 - 如果计算的业务逻辑发生更改,可能不希望对现有记录追溯更改 TotalCash 属性。

    只是把它放在那里......

    【讨论】:

      【解决方案6】:

      第一个,因为:
      1) 代码越少越好;
      2) 复杂性更低;
      3) 更少的变量有助于减少附带问题;
      4) 属性会一直更新;
      5)如果您更改“CalcCashTotal”程序名称,您将获得更多其他点来更改...

      【讨论】:

      • 大多数 IDE 会为您重命名,更新所有引用,使 #5 几乎成为 IMO 的一个有争议的问题。
      • 他们会在所有引用你的类的代码中改变它吗?即使是您不管理的代码,例如客户针对您的班级编写的自定义代码?
      【解决方案7】:

      放在get函数上不是很理想。您将无缘无故地重新计算它。它甚至没有任何意义。所以这里是 ::gasp:: 优化有意义并且是首选的情况。计算一次,即可获得收益。

      【讨论】:

        【解决方案8】:

        我一直被告知,如果有大量的工作或计算正在完成,你应该用一种方法来做。据我所知,没有主要的编译器/运行时优势,但它对代码的使用者来说更有意义。需要一段时间才能将值返回给我的属性会引发一个危险信号,表明可能有问题。

        那是我的 2 美分……但如果课程真的那么简单,我什至可能只会使用你的第一个示例 :-)

        【讨论】:

          【解决方案9】:

          第一个选项更好。 “优化”的差异是微乎其微的。更不用说,如果设置反复发生但您只需要获取 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 对象的属性将是相当灾难性的。 getter 必须确定自上次 get 语句以来宽度是否发生了变化,增加了复杂性

          【讨论】:

          • 玩魔鬼代言,如果设置只发生几次,但得到一遍又一遍怎么办?
          • 在那种情况下,您必须考虑复杂性。哪种方式不那么复杂并且更有意义?除非它真的很重要,否则不能为了性能而牺牲一个简单的解决方案。我会在代码上调用 YAGNI,因为它会节省一些处理器周期。
          【解决方案10】:

          这取决于。您的应用程序是读取繁重还是写入繁重?计算成本高吗? 如果计算很昂贵并且您的应用程序被大量读取,请在片场进行,这样您只需支付几次计算惩罚(与读取相比)。

          【讨论】:

            【解决方案11】:

            我将对只读 get 或 set 进行计算。

            我认为一个属性的行为应该像它有一个支持变量。

            我不喜欢读取时间过长的计算。

            【讨论】:

              【解决方案12】:

              我会采用在 TotalCash 的 getter 中进行计算的方法,因为更少的代码几乎总是更好。它还确保 TotalCash 的值始终正确。作为一个人为的示例,如果您有另一个方法 NewOrder(Price, Qty) 并且您忘记在此方法结束时调用 CalculateTotal,您很容易以错误的 TotalCash 值结束。

              如果计算需要一些时间来处理并且只更改一个或两个属性的值需要重新计算,那么在 setter 中计算它可能会更好,但几乎总是最好选择留下更少出错空间的方法,即使执行时间稍长。

              【讨论】:

                【解决方案13】:

                我的规则,我向任何人推荐这个:

                方法 = 有副作用 Getters = 没有副作用(除了 memoization - 在 getters 中也是允许的)

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2010-09-11
                  • 2013-07-01
                  • 2011-09-27
                  • 1970-01-01
                  • 2017-03-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多