【问题标题】:Initializing multiple objects at once?一次初始化多个对象?
【发布时间】:2020-05-06 22:42:59
【问题描述】:

所以,我正在尝试初始化多个对象并将它们添加到列表中。我想要发生的是通过运行Market.new,我希望将 api 中的每个项目都添加为对象。下面是我认为可能有效的代码。但是,它将相同的对象添加到列表中 100x。有没有办法做到这一点?

def initialize
    data = JSON.parse(open(BASE_URL + "markets?vs_currency=usd").read)
    i = 0
    # looping until we hit the end of the list. adding them all as objects.
    while i < data.length
        @id = data[i]["id"].to_s
        @name = data[i]["name"].to_s
        @symbol = data[i]["symbol"].to_s
        @price = data[i]["current_price"].to_s
        @price_movement_24h = data[i]["price_change_percentage_24h"].to_s
        @market_cap = data[i]["market_cap"].to_s
        @@market << self
        i += 1
    end
end

这给了我添加到 @@market 列表 100 倍的相同对象。

=> [#<Market:0x0000561b3f853f30
  @id="iostoken",
  @market_cap="43969067",
  @name="IOST",
  @price="0.0036523",
  @price_movement_24h="-0.76702",
  @symbol="iost">,
 #<Market:0x0000561b3f853f30
  @id="iostoken",
  @market_cap="43969067",
  @name="IOST",
  @price="0.0036523",
  @price_movement_24h="-0.76702",
  @symbol="iost">,

【问题讨论】:

    标签: ruby oop


    【解决方案1】:

    首先我想说这是非常奇怪的 Ruby 代码,而不是您通常会做的事情。这并不是侮辱,只是说 Ruby 开发人员倾向于遵循相同或相似的对象结构准则,而这段代码感觉就像是从另一种语言移植而来的。

    您看到的问题是由于在initialize 方法中您没有创建任何新对象,而是更新实例变量并将self 推送到类变量中。 self 直接引用此实例,这意味着类变量数组正在填充对同一对象的引用。如果您坚持保持代码不变,那么您应该在更新实例变量后推送对象的副本。

    @@market << self.dup
    

    这会创建一个具有不同内存地址和引用的重复对象。

    如果您希望编写更多惯用的代码,您可能希望使用多个对象,而根本不依赖类变量。如果您没有在字符串中插入变量,请使用单引号而不是双引号。保持对象方法简单并专注于特定任务。这些只是 Ruby 开发人员在编写代码时考虑的一些事情,但要找到最适合您的。

    以这样的事情为例:

    class Market
      attr_accessor :id, :name, :symbol, :price, :price_movement_24h, :market_cap
    
      def initialize(data = {})
        @id = data['id'].to_s
        @name = data['name'].to_s
        @symbol = data['symbol'].to_s
        @price = data['current_price'].to_s
        @price_movement_24h = data['price_change_percentage_24h'].to_s
        @market_cap = data['market_cap'].to_s
      end
    end
    
    class ImportService
      def self.from_api(url)
        response = JSON.parse(open(url).read) || []
        response.map { |data| Market.new(data) }
      end
    end
    

    你可以这样称呼它:

    @market_data = ImportService.from_api(BASE_URL + 'markets?vs_currency=usd')
    

    【讨论】:

    • 我不明白@@market &lt;&lt; self.dup 的意义(假设它在initialize 中计算过一次)。假设c = C.new; c.instance_variable_set(:@id, 'cat'),其中C 是类。这不会影响在initialize 中添加到@@marketc 的副本。那么保存c的副本有什么意义呢?
    • 运行代码,自己看看。如果没有dup,您不会创建 c 的副本,只是推送对它的引用。检查这个例子,尝试使用和不使用dup。当不使用 dup 时,您会看到输出显示相同的对象内存空间。 pastebin.com/ujLjDKi1
    【解决方案2】:

    self 添加到@@market 时,应将代码更改为此

    @@market << self.dup
    

    但是,我认为在这里使用类变量并添加self 来初始化对象数组不是一个好习惯。相反,您应该创建一个新类(例如MarketImporter

    class Market
      attr_accessor :id, :name, :symbol, :price, :price_movement_24h, :market_cap
    
      def initialize(data = {})
        @id = data["id"].to_s
        @name = data["name"].to_s
        @symbol = data["symbol"].to_s
        @price = data["current_price"].to_s
        @price_movement_24h = data["price_change_percentage_24h"].to_s
        @market_cap = data["market_cap"].to_s
      end
    end
    
    class MarketImporter
      attr_accessor :markets
    
      def initialize
        data = JSON.parse(open(BASE_URL + "markets?vs_currency=usd").read)
        @markets = data.collect { |item| Market.new(item) }
      end
    end
    

    然后你可以通过

    来初始化集合
    MarketImporter.new
    

    【讨论】:

    • 是的,我知道你的意思。但是,这是一个类,项目要求要求使用某些东西,所以我试图在这些限制内做到这一点。 .dup 完美运行。谢谢你的建议,这更有意义。
    • 为什么建议把@@market &lt;&lt; self改成@@market &lt;&lt; self.dup(和@@market &lt;&lt; dup一样)?如果你害怕@@market = "cat"(和@@market &lt;&lt; self)会改变self,你可以高枕无忧; self 不能直接更改。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-20
    • 2016-02-14
    • 1970-01-01
    • 1970-01-01
    • 2016-12-18
    • 2011-09-11
    相关资源
    最近更新 更多