我正在努力成为一名更好的开发者,我总是问自己,如果有更好的方法来做这些事情。这不是我第一次处理这个问题所以我决定问你对它的看法。
假设我必须实现一个代表产品的类。
class Product
def initialize (name, net_price)
@name = name
@net_price = net_price
@gross_price = nil
end
def set_gross_price
@gross_price = heavy_gross_price_calculation
end
def export
@gross_price.nil? && set_gross_price
return product.to_hash
end
def heavy_gross_price_calculation
# This function calculate the gross price but let's say that this is
# pretty onerous operation that involves maybe also an external API
# request
end
end
假设这个类的工作流程是创建一个产品,计算总价并将其导出以备将来使用。
不调用set_gross_price
中的initialize
方法是否正确?
事实是,当您导出产品时,必须计算总价格,但我不认为正确的选择是强迫开发人员在set_gross_price
之前调用export
,但我也不是确定export
方法的第一行,因为该集应关注设置总价而不检查它是否为空。
你有更好的方法来实现这个吗?
由于
答案 0 :(得分:0)
我看到了几种方法:
Product
成为一个愚蠢的对象,只保存有关产品的信息,从外部计算价格gross_price
成为 memoizes 计算结果的方法愚蠢对象
class Product
def initialize(name, net_price, gross_price)
@name ...
...
end
end
class GrossPriceCalculator
def initialize()
end
def call(net_price)
# complicate operation goes here
# ...
end
end
<强>记忆化强>
class Product
def initialize(name, net_price)
@name = name
@net_price = net_price
end
def gross_price
@gross_price ||= calculate_gross_price
end
private
def calculate_gross_price
# here is the complicate operation..
#
end
end
我更喜欢第一种解决方案。它不会触发由某些完全不同的操作(如导出)隐藏的潜在昂贵的调用
答案 1 :(得分:0)
完全同意如果出口需要繁重的计算,那么你不应该在初始化中做到这一点(作为具体案例,考虑编写单元测试;为什么所有的测试都要付出沉重的代价单个方法需要时计算?)。
避免@gross_price.nil? && set_gross_price
之类的事情的一种方法是,如果变量为空,则使用||=
执行一次性分配(请参阅例如What does ||= (or-equals) mean in Ruby?)。同时,通过使用属性访问器,您可以在执行计算时抽象自己,并在getter上进行惰性计算:
class Product
attr_reader :name, :net_price
def initialize (name, net_price)
@name = name
@net_price = net_price
@gross_price = nil
end
def gross_price
@gross_price ||= heavy_gross_price_calculation
end
def export
return to_hash
end
def heavy_gross_price_calculation
puts "heavy calculation"
1000
end
def to_hash
Hash[instance_variables.map do |var_name|
# Remove the "@"
name = var_name.to_s[1..-1]
[name, send(name)]
end]
end
end
我在这里给出了一个可能的to_hash
实现,但当然你可以使用另一个,只要它使用访问器而不是直接使用inst vars。
然后在控制台中你可以做(看到“重度计算”只打印一次,第一次调用export
):
2.2.3 :001 > p = Product.new('X', 100)
=> #<Product:0x0000000104e128 @name="X", @net_price=100, @gross_price=nil>
2.2.3 :002 > p.export
heavy calculation
=> {"name"=>"X", "net_price"=>100, "gross_price"=>1000}
2.2.3 :003 > p.export
=> {"name"=>"X", "net_price"=>100, "gross_price"=>1000}
2.2.3 :004 >
这样做的一个优点是除了export
之外需要gross_price
的任何其他方法也可以获得它,并且export
已经具有所有好处并且计算只需要执行一次
答案 2 :(得分:0)
我正努力成为更好的开发者......
你走在正确的轨道上。
您已检测到SRP的气味。您的Product
至少做了两件事:
我认为如果你创造1或2个课程,气味就会消失。
您可能需要阅读POODR。并编写规范。