Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/ssl/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ruby 构造函数中的实例\变量\集_Ruby - Fatal编程技术网

Ruby 构造函数中的实例\变量\集

Ruby 构造函数中的实例\变量\集,ruby,Ruby,我制作了一个这样的构造函数: class Foo def initialize(p1, p2, opts={}) #...Initialize p1 and p2 opts.each do |k, v| instance_variable_set("@#{k}", v) end end end 我想知道像这样动态设置实例变量是否是一种好的做法,或者我是否应该像大多数LIB那样一个一个地手动设置它们,以及为什么。这个问题的答案总是基于某人的个人意见,所

我制作了一个这样的构造函数:

class Foo
  def initialize(p1, p2, opts={})
    #...Initialize p1 and p2
    opts.each do |k, v|
      instance_variable_set("@#{k}", v)
    end
  end
end

我想知道像这样动态设置实例变量是否是一种好的做法,或者我是否应该像大多数LIB那样一个一个地手动设置它们,以及为什么。

这个问题的答案总是基于某人的个人意见,所以这里是我的

简洁明了 如果你不能提前知道一组选项,那么你就别无选择,只能照你所做的去做。然而,如果选项是从已知集合中提取的,那么我会倾向于清晰而不是简洁,并且有明确的方法来设置选项。这也是添加任何rdoc等的好地方

安全
从安全角度来看,拥有处理选项设置的方法将允许您根据需要执行验证。

当您需要执行此类操作时,参数清单会有所不同。在这种情况下,Ruby(以及大多数现代语言)中已经有了一个方便的结构:数组和哈希。在这种情况下,您应该将整个选项保存为一个散列。这将使事情变得更简单。

诊断问题 您在这里所做的是一个相当简单的元编程示例,即基于某些输入动态生成代码。元编程通常会减少需要编写的代码量,但会使代码更难理解

在这种特殊情况下,它还引入了一些耦合问题:类的公共接口与内部状态直接相关,这使得在不更改另一个状态的情况下很难更改其中一个

重构示例 考虑一个稍长的示例,其中我们使用一个实例变量:

class Foo
  def initialize(opts={})
    opts.each do |k, v|
      instance_variable_set("@#{k}", v)
    end
  end

  def greet(name)
    greeting = @greeting || "Hello"
    puts "#{greeting}, name"
  end
end

Foo.new(greeting: "Hi").greet
在这种情况下,如果有人想将
@greeting
实例变量重命名为其他变量,他们可能很难理解如何进行重命名。很明显,
@greeting
方法使用了
@greeting
,但是搜索
@greeting
的代码并不能帮助他们找到它的第一个设置位置。更糟糕的是,为了改变这一点内部状态,他们还必须改变对
Foo.new
的任何调用,因为我们所采取的方法将内部状态与公共接口联系起来

删除元编程 让我们看看另一种选择,我们只存储所有的
选项
,并将它们视为状态:

class Foo
  def initialize(opts={})
    @opts = opts
  end

  def greet(name)
    greeting = @opts.fetch(:greeting, "Hello")
    puts "#{greeting}, name"
  end
end

Foo.new(greeting: "Hi").greet
通过删除元编程,这稍微澄清了情况。第一次希望更改此代码的新团队成员将有一段稍微轻松的时间,因为他们可以使用编辑器功能(如查找和替换)重命名内部IVAR,并且传递给初始值设定器的参数与内部状态之间的关系更加明确

减少耦合 我们可以更进一步,将内部构件与接口分离:

class Foo
  def initialize(opts={})
    @greeting = opts.fetch(:greeting, "Hello")
  end

  def greet(name)
    puts "#{@greeting}, name"
  end
end

Foo.new(greeting: "Hi").greet
在我看来,这是我们研究过的最好的实现:

  • 没有元编程,这意味着我们可以找到正在设置和使用的变量的显式引用,例如使用编辑器的搜索功能,
    grep
    git log-s
    ,等等
  • 我们可以在不改变接口的情况下改变类的内部结构,反之亦然
  • 通过在初始化器中调用
    opts.fetch
    ,我们向我们类的未来读者明确了
    opts
    参数应该是什么样子,而不是让他们阅读整个类
  • 何时使用元编程 元编程有时很有用,但这种情况很少。作为一个粗略的指导,我更倾向于在框架或库代码中使用元编程,这通常需要更通用(例如),并避免在应用程序代码中使用元编程,而应用程序代码通常更特定于特定的问题或领域


    即使是在库代码中,我也希望能够清晰地重复几行。

    您可以使用
    attr\u accessor
    声明可用的实例变量,并动态调用setter:

    class Foo
      attr_accessor :bar, :baz, :qux
    
      def initialize(opts = {})
        opts.each do |k, v|
          public_send("#{k}=", v)
        end
      end
    end
    
    Foo.new(bar: 1, baz: 2) #=> #<Foo:0x007fa8250a31e0 @bar=1, @baz=2>
    Foo.new(qux: 3)         #=> #<Foo:0x007facbc06ed50 @qux=3>
    
    class-Foo
    属性存取器:bar,:baz,:qux
    def初始化(opts={})
    选择每个do | k,v|
    公共发送(“#{k}=”,v)
    结束
    结束
    结束
    Foo.new(酒吧:1,酒吧:2)#=>#
    Foo.new(qux:3)#=>#
    
    如果传递了未知选项,此方法也会显示错误:

    Foo.new(quux: 4) #=> undefined method `quux=' for #<Foo:0x007fd71483aa20> (NoMethodError)
    
    Foo.new(qux:4)#=>for#(NoMethodError)的未定义方法'qux='
    
    很有意义,我现在倾向于明确设置选项。我将等待其他观点,然后再将其设置为已解决。谢谢稍后您将如何处理所有这些实例变量?基本上,它们将影响我的对象行为,具体取决于它们是否已定义以及它们的值是什么。我不知道它是否真的回答了您的问题:)您的意思是将我的选项哈希保存为实例变量?这对我来说并不简单,我想我最终会得到一个不可读的详细代码,比如@opts[:my_opt],而不是@my_opt?有什么问题吗
    @opts[:my_opt]
    ?如果这看起来太长,你可以把名字改成一个字母:
    @h[:my_opt]
    @Logar它更像是
    @opts[:my_opt]
    实例变量(:my_opt)
    ,也就是说,你不能使用
    @my_opt
    ,因为它事先不知道,是吗?这是预先知道的,这样我倾向于同意@Chris answer并明确设置它们。是的,如果我不知道选项集,我同意保存整个散列是一个可接受的解决方案,但是如果我知道,它仍然会让我无法调用散列,而不是在初始化时定义变量。当我可以在初始值设定项中用几行调用变量时,我看不到调用哈希的意义。考虑到Sawa的经验和我的经验,我一定错了,但我无法理解why@Logar这取决于特定的用例。因为我不知道你的代码的全貌,也许你能告诉我更好的答案。最好的答案对我有帮助