我们说我定义了类:
Class Product < Struct.new(:field1,:field2)
以及相应的new
方法,它将输入string
解析为此类的属性:
def initialize(string)
splitted=string.split(',')
@field1=splitted[0]
@field2=splitted[1]
end
我希望使用相同的new
方法来解析其他类,包括Product
以及响应to_s
的所有内容:
def initialize(string)
case
when string.is_a?(String)
splitted=string.split(',')
@field1=splitted[0]
@field2=splitted[1]
when string.is_a?(Product)
@field1,@field2=string.deal
when string.respond_to?(:to_s)
@field1,@field2=Product.new(string.to_s).deal
else
raise RuntimeError,"Cannot understand product '#{string}' of class '#{string.class}'.", caller
end
end
def deal
return @field1,@field2
end
现在我也想要这个新方法来处理数组:
productarray=Product.new(["some field,another field","name, surname",:"up, down",Product.new("something, anything")])
我最初的猜测是在when
方法中添加另一个new
子句:
when string.is_a?(Array)
return string.map{|s| Product.new(s)}
但这并不奏效。
问题:
initialize
类方法来处理self
的数组(如上所述)?case
中使用when
/ new
结构来处理每个不同的类;还有其他选择吗?self=string
并且必须经历deal
的所有麻烦?< / LI>
参考文献:
答案 0 :(得分:1)
您需要做出一系列设计决策。首先,使用Adapter模式将“Product”或“String”转换为Product类可识别的args列表:
class Product < Struct.new(:field1,:field2)
def deal
return field1, field2
end
end
class StringToFieldsAdapter < Struct.new(:string)
def run
string.split(',')
end
end
class ProductToFieldsAdapter < Struct.new(:product)
def run
product.deal
end
end
让我们测试一下这些适配器:
string_adapter = StringToFieldsAdapter.new('hey,there').run #=> [hey, there]
product_adapter = ProductToFieldsAdapter.new(Product.new(*StringToFieldsAdapter.new('one,two').run)).run #=> [one, two]
product_from_string = Product.new(*string_adapter) # the splat * just turns the [hey, there] array into arguments
product_from_product = Product.new(*product_adapter) #=> #<struct Product field1="one", field2="two">
现在,在您使用数组的示例中,您并不真正想要在产品中接受数组。你想要一些接受数组并从每个元素中创建一个新对象的方法。
为此,我创建了一个类,它将识别要使用的适配器,然后是另一个实际创建对象的类(是工厂):
arr = ["some field,another field","name, surname","up, down",Product.new(*StringToFieldsAdapter.new("something, anything").run)]
class AdapterRecognizer < Struct.new(:object)
def run
case object
when String then StringToFieldsAdapter
when Product then ProductToFieldsAdapter
end
end
end
class ProductFactory < Struct.new(:array)
def create_objects_from_each_array_element
array.map do |element|
adapter_to_use = AdapterRecognizer.new(element).run
Product.new(*adapter_to_use.new(element).run)
end
end
end
p ProductFactory.new(arr).create_objects_from_each_array_element #=> should produce bunch of products
我只为字符串和产品制作了适配器,您可以轻松地为符号等扩展。例如,添加符号只是添加新适配器类的简单实例:
class SymbolToFieldsAdapter < Struct.new(:symbol)
def run
symbol.to_s.split(',').map(&:to_sym)
end
end
然后在适配器识别器中添加另一个'when':
when Symbol then SymbolToFieldsAdapter
答案 1 :(得分:0)
问题在于when
的顺序。由于ruby中的所有内容都响应to_s
,尤其是Array
,因此您的数组对象会在最后一个语句之前被捕获并尝试将其作为字符串处理。
编辑:问题是您无法从初始化程序中有意义地返回,您可以使用它作为一种方法来阻止程序运行以下语句,但.new
不会使用您返回的内容。另一方面,您可以使用类方法为您构建对象列表,如下所示。然而,为此目的建立工厂更好,但这也有效。
话虽如此,正如@daremkd所提到的,做这件事并不是一个非常好的设计决定。既然你处理了所有这些特殊情况,你就会在未来将代码打开到很多可能的错误中,更重要的是使这个方法的心理模型变得非常复杂。
class Product
def initialize(field1, field2)
@field1 = field1
@field2 = field2
end
def self.new_from_parameters(string)
case
when string.is_a?(String)
splitted=string.split(',')
Product.new(splitted[0],splitted[1])
when string.is_a?(Product)
string
when string.respond_to?(:to_s)
Product.new_from_parameters(string.to_s)
else
raise RuntimeError,"Cannot understand product '#{string}' of class '#{string.class}'.", caller
end
end
end