我正在使用Product类在Ruby中编写程序。每当使用错误类型的参数初始化Product时,我都会引发一些异常。有没有办法可以减少我提出的异常(我甚至指的是正确的?)我很感激你的帮助。代码如下:
class Product
attr_accessor :quantity, :type, :price, :imported
def initialize(quantity, type, price, imported)
raise ArgumentError.new("Type must be a string") if type.class != String
raise ArgumentError.new("Quantity must be greater than zero") if quantity <= 0
raise ArgumentError.new("Price must be a float") if price.class != Float
@quantity = quantity
@type = type
@price = price.round(2)
@imported = imported
end
end
答案 0 :(得分:5)
惯用的方法是根本不进行类型检查,而是强制传递的对象(使用to_s
,to_f
等):
class Product
attr_accessor :quantity, :type, :price, :imported
def initialize(quantity, type, price, imported)
raise ArgumentError.new("Quantity must be greater than zero") unless quantity > 0
@quantity = quantity
@type = type.to_s
@price = price.to_f.round(2)
@imported = imported
end
end
然后您将获得适当的String / Float / etc。传递的对象的表示,如果他们不知道如何被强制转换为那些类型(因为他们没有响应那个方法),那么你将适当地得到一个NoMethodError。
对于数量检查,这看起来很像验证,你可能想要提取到一个单独的方法(特别是如果它们有很多):
class Product
attr_accessor :quantity, :type, :price, :imported
def initialize(quantity, type, price, imported)
@quantity = quantity
@type = type.to_s
@price = price.to_f.round(2)
@imported = imported
validate!
end
private
def validate!
raise ArgumentError.new("Quantity must be greater than zero") unless @quantity > 0
end
end
答案 1 :(得分:1)
class Product
attr_accessor :quantity, :type, :price, :imported
def initialize(quantity, type, price, imported)
raise ArgumentError.new "Type must be a string" unless type.is_a?(String)
raise ArgumentError.new "Quantity must be greater than zero" if quantity.zero?
raise ArgumentError.new "Price must be a float" unless price.is_a?(Float)
@quantity, @type, @price, @imported = quantity, type, price.round(2), imported
end
end
答案 2 :(得分:1)
你可以做类似下面的事情,虽然我希望有宝石可以做到这一点以及更多,并做得更好:
module ArgCheck
def type_check(label, arg, klass)
raise_arg_err label + \
" (= #{arg}) is a #{arg.class} object, but should be be a #{klass} object" unless arg.is_a? klass
end
def range_check(label, val, min, max)
raise_arg_err label + " (= #{val}) must be between #{min} and #{max}" unless val >= min && val <= max
end
def min_check(label, val, min)
puts "val = #{val}, min = #{min}"
raise_arg_err label + " (= #{val}) must be >= #{min}" unless val >= min
end
def max_check(val, min)
raise_arg_err label + " (= #{val}) must be <= #{max}" unless val <= max
end
# Possibly other checks here
private
def raise_arg_err(msg)
raise ArgumentError, msg + "\n backtrace: #{caller_locations}"
end
end
class Product
include ArgCheck
attr_accessor :quantity, :type, :price, :imported
def initialize(quantity, type, price)
# Check arguments
min_check 'quantity', quantity, 0
type_check 'type', type, String
type_check 'price', price, Float
@quantity = quantity
@type = type
@price = price.round(2)
end
end
product = Product.new(-1, :cat, 3)
# => arg_check.rb:23:in `raise_arg_err': quantity (= -1) must be >= 0 (ArgumentError)
# backtrace: ["arg_check.rb:11:in `min_check'", "arg_check.rb:33:in `initialize'", \
# "arg_check.rb:43:in `new'", "arg_check.rb:43:in `<main>'"]
product = Product.new(1, :cat, 3)
# => arg_check.rb:26:in `raise_arg_err': type (= cat) is a Symbol object, \
# but should be be a String object (ArgumentError)
# backtrace: ["arg_check.rb:3:in `type_check'", "arg_check.rb:34:in `initialize'", \
# "arg_check.rb:48:in `new'", "arg_check.rb:48:in `<main>'"]
product = Product.new(1, "cat", 3)
# => arg_check.rb:23:in `raise_arg_err': price (= 3) must be a Float object (ArgumentError)
# backtrace: ["arg_check.rb:3:in `type_check'", "arg_check.rb:35:in `initialize'", \
# "arg_check.rb:53:in `new'", "arg_check.rb:53:in `<main>'"]
product = Product.new(1, "cat", 3.00) # No exception raised
请注意,在irb中运行时,Kernel#caller_locations
会带来许多你不想要的东西,从命令行运行时你不会得到它。