在Ruby中,如何将属性标记为脏的?

在Ruby中,如何将属性标记为脏的?,ruby,Ruby,假设我有以下课程: class Cashier def purchase(amount) (@purchases ||= []) << amount end def total_cash (@purchases || []).inject(0) {|sum,amount| sum + amount} end end 班级出纳 def采购(金额) (@purchases | |=[])最简单的方法是维护另一个变量,以指示@purchases是否

假设我有以下课程:

class Cashier

  def purchase(amount)
    (@purchases ||= []) << amount
  end


  def total_cash
    (@purchases || []).inject(0) {|sum,amount| sum + amount}
  end

end
班级出纳
def采购(金额)

(@purchases | |=[])最简单的方法是维护另一个变量,以指示
@purchases
是否脏。例如:

class Cashier

  def initialize(*args)
    # init @purchases and @total_cash
    @is_purchases_dirty = false
  end

  def purchase(amount)
    (@purchases ||= []) << amount
    @is_purchases_dirty = true
  end


  def total_cash
    return @total_cash unless @is_purchases_dirty
    @is_purchases_dirty = false
    @total_cash = (@purchases || []).inject(0) {|sum,amount| sum + amount}
    return @total_cash
  end

end

我还建议你不要使用昂贵的手术命名方案。我会将
total\u cash
重命名为类似
calc\u total\u cash
的名称,以便告诉API用户,这是一个相对昂贵的调用,而不是简单的getter/setter。

在最简单的情况下,您可以为要标记为dirty的对象定义一个实例变量。修改变量时(在
购买方法中)将其设置为
true

检查
现金总额中的值
;如果是,请使用total的缓存版本。否则,计算新值并将其存储在缓存中

class Cashier

  def purchase(amount)
    @purchases_dirty = true
    (@purchases ||= []) << amount
  end


  def total_cash
    @total_cash = (@purchases || []).inject(0) do |sum,amount|
      sum + amount
    end if (@purchases_dirty || @total_cash.nil?)
    @purchases_dirty = false
    @total_cash
  end

end
班级出纳
def采购(金额)
@购买脏的=真的

(@purchases | |=[])如果您愿意,您可以比其他答案更进一步。您可以编写更改代码的代码,而不是将代码更改为仅在必要时重新计算。每个人都喜欢元编程

下面是一些代码,它使用执行可能很长计算的方法的名称,以及调用时使以前的任何计算无效的方法的名称列表,并编写代码来包装这些方法,仅在必要时执行计算,如果没有,则返回存储值

module ExpensiveCalculation

  def recalc_only_if_necessary(meth, *mutators)

    aliased_method_name = "__#{meth.object_id}__"
    value = "@__#{meth.object_id}_value__"
    dirty_flag = "@__#{meth.object_id}_dirty__"

    module_eval <<-EOE
      alias_method :#{aliased_method_name}, :#{meth}
      private :#{aliased_method_name}

      def #{meth}(*args, &blk)
        #{dirty_flag} = true unless defined? #{dirty_flag}
        return #{value} unless #{dirty_flag}
        #{value} = #{aliased_method_name}(*args, &blk)
        #{dirty_flag} = false
        #{value}
      end        
    EOE

    mutators.each do |mutator|
      aliased_mutator = "__#{meth.object_id}_#{mutator.object_id}__"

      module_eval <<-EOE
        alias_method :#{aliased_mutator}, :#{mutator}
        private :#{aliased_mutator}

        def #{mutator}(*args, &blk)
          #{dirty_flag} = true
          #{aliased_mutator}(*args, &blk)
        end
      EOE
    end
  end

  # this hook is used to make the new method
  # private to the extended class.
  def self.extend_object(obj)
    super
    obj.private_class_method :recalc_only_if_necessary
  end

end
模块费用计算
def recalc仅在必要时使用(meth,*突变剂)
别名_method_name=“uu#{meth.object_id}uu”
value=“@_35;{meth.object_id}u value_35;”
dirty_flag=“@_35;{meth.object_id}u dirty_35;”

模块评估对Naming提出了很好的建议,可能没有必要每次都从头开始重新计算总数。只要用
@total\u cash+=amount
@hammer:呃,是的。。。我甚至没有真正了解代码在做什么=D。这一点很好。无关的注释:如果您想更简洁,可以执行
inject(0,:+)
module ExpensiveCalculation

  def recalc_only_if_necessary(meth, *mutators)

    aliased_method_name = "__#{meth.object_id}__"
    value = "@__#{meth.object_id}_value__"
    dirty_flag = "@__#{meth.object_id}_dirty__"

    module_eval <<-EOE
      alias_method :#{aliased_method_name}, :#{meth}
      private :#{aliased_method_name}

      def #{meth}(*args, &blk)
        #{dirty_flag} = true unless defined? #{dirty_flag}
        return #{value} unless #{dirty_flag}
        #{value} = #{aliased_method_name}(*args, &blk)
        #{dirty_flag} = false
        #{value}
      end        
    EOE

    mutators.each do |mutator|
      aliased_mutator = "__#{meth.object_id}_#{mutator.object_id}__"

      module_eval <<-EOE
        alias_method :#{aliased_mutator}, :#{mutator}
        private :#{aliased_mutator}

        def #{mutator}(*args, &blk)
          #{dirty_flag} = true
          #{aliased_mutator}(*args, &blk)
        end
      EOE
    end
  end

  # this hook is used to make the new method
  # private to the extended class.
  def self.extend_object(obj)
    super
    obj.private_class_method :recalc_only_if_necessary
  end

end
class Cashier
  extend ExpensiveCalculation

  def purchase(amount)
    (@purchases ||= []) << amount
  end

  def total_cash
    (@purchases || []).inject(0) {|sum,amount| sum + amount}
  end

  recalc_only_if_necessary :total_cash, :purchase
end