如何创建在子级之间不共享状态的类变量

时间:2019-05-07 19:44:16

标签: ruby

我希望创建一种方法来使Children类在类级别上表达一些业务定义。

我试图为此使用类变量,但是我发现它们在所有类之间共享状态,因此一旦定义了第二类,“ @@ attribute”类var就会更改所有相邻类实例的值。

>
class Parent
  def self.type(value)
    @@_type = value
  end

  def render
    puts @@_type
  end
end

class Children < Parent
  type "name"
end

Children.new.render # Result: name. Expected: name

class Children2 < Parent
  type "title"
end

Children2.new.render # Result: title. Expected: title
Children.new.render # Result: title. Expected: name

如何以最简单直接的方式创建此DSL? 这是HTTParty,Virtus等几种Ruby Gems的常见模式。

我什至试图查看他们的源代码以了解其工作方式,但是对于我想要的东西来说似乎太复杂了。

感谢您的帮助!

2 个答案:

答案 0 :(得分:1)

类变量是Ruby工具的老三级之一,大多数经验丰富的Rubiesst很少使用(如果有的话)。 1 。相反,您想使用类级实例变量Parent是类Class的实例。

class Parent
  def self.type=(value)
    @type = value
  end

  def self.type
    @type
  end

  def render
    puts self.class.type
  end
end

class Children < Parent
  self.type = "name"
end

Children.new.render
  #=> "name"

class Children2 < Parent
  self.type = "title"
end

Children2.new.render
  #=> "title"
Children.new.render
  #=> "name"

首先,类方法type=被称为“ setter”,而类方法“ type”被称为“ getter”。您有一个二传手type,在吵架。如果这样做,您将如何获得其价值?要将其用作吸气剂,您还必须执行以下操作:

  def self.type=(value=nil)
    if value.nil?
      @type
    else
      @type = value
    end
  end

在这里定义一个吸气剂会更有意义

  def self.type
    @type
  end

并且没有设置器,只写例如@type = "name"

这很麻烦,仅当您不想将@type设置为nil时才有效。您也可以将方法保留为设置方法,并使用self.class.instance_variable_get(:@type)来获取其值,但这同样令人恐惧。最好有一个setter和一个getter。

使用setter时,我们需要在type前面加上self.来告诉Ruby,我们希望调用getter而不是将局部变量type设置为给定值。当然,我们可以只写“ @type =“ title”。

创建setter和getter的常规方法是编写attr_accessor :type(调用类方法Module#attr_accessor)。由于类方法存储在类的单例类中,因此可以按以下方式进行 2

class Parent
  class << self
    attr_accessor :type
  end
  def render
    puts self.class.type
  end
end

class Children < Parent
  self.type = "name"
end

Children.new.render
  #=> "name"

class Children2 < Parent
  self.type = "title"
end

现在考虑实例方法Parent#render。作为实例方法,它的接收者是类(或子类)的实例,例如parent = Parent.new。这意味着在方法render中调用self等于parent。但是,我们要调用类方法type。因此,我们必须将parent转换为Parent。{p}

1。另外两个(当然,我认为)是全局变量和self.class循环。他们在Ruby新手中的流行可能是由于他们倾向于在许多学习Ruby的书的第1章中首次亮相。

2。在for的单例类中定义attr_accessor的方法有很多。其他两个是Parentsingleton_class.instance_eval do { attr_accessor :type }

答案 1 :(得分:-1)

好吧,我不知道为什么会这样,但是它是这样工作的:

class Parent
  class << self
    attr_accessor :_type

    def type(value)
      self._type = value
    end
  end

  def render
    puts self.class._type
  end
end

我真的很想理解为什么,但是“ self.class”和“ class << self”对我来说似乎很黑暗。

光?