【问题标题】:Rubyish way of adding many variables to a class向类中添加许多变量的 Rubyish 方式
【发布时间】:2015-12-16 19:48:13
【问题描述】:

我一直在开发一款需要模拟一长串国家/地区的游戏(如果有的话,更像是一个网络玩具),我已经设法让它工作,但我不禁感到我的解决方案既不是 Rubyish 也不是优雅的方式。

代码看起来有点像这样:

class Countries
    include Singleton

    def get(tag)
        return eval "@#{tag}"
    end

    def initialize
        @array = []

        @afghanistan = Country.new("Afghanistan", [:authoritarian, :reactionary, :sunni, :capitalist, :militarist, :southern_asia, :unstable])
        @afghanistan.gdp = 20444
        @afghanistan.population = 26023
        @array << :afghanistan

        @albania = Country.new("Albania", [:hybrid, :conservative, :sunni, :capitalist, :pacifist, :southern_europe])
        @albania.gdp = 13276
        @albania.population = 2893
        @array << :albania
    #and so on and so forth
    end
    attr_accessor :array
end

countries = Countries.instance
puts countries.get("usa").name
puts
for i in 0..(countries.array.size-1)
    puts countries.get(countries.array[i]).name
end

我得到了预期的输出

United States

Afghanistan
Albania
Algeria
...

但理想情况下,一个优雅的解决方案不需要 .get(),而且这看起来不像是解决这个问题的类似 Ruby 的方式。有没有更好的做法?

我主要从 Stack Overflow、Ruby 文档和测试中学到了一些知识,所以我很可能在此过程中违反了很多最佳实践。 Country 类的初始化程序采用一个字符串作为名称和一个要添加的标签数组,而其他属性打算在单独的行中添加。

【问题讨论】:

  • 绝对要做的第一件事就是将国家/地区存储在哈希中
  • @DamianoStoffie 你的意思是替换数组还是替换类?
  • @spickermann 的解决方案是我推荐的解决方案,但这里有一些关于类设计的更大问题。 SOLID principles 可能值得一读,尤其是单一职责原则和 DRY

标签: ruby coding-style


【解决方案1】:

我会将国家/地区的详细信息存储在文件(例如 countries.yml 或 csv 文件)或数据库中:

# in countries.yml
afganistan:
  name: Afganistan
  tags:
    - authoritarian
    - reactionary
    - sunni
    - capitalist
    - militarist
    - southern_asia
    - unstable
  gdp: 20444
  population: 26023
albania:
  name: Albania
  tags:
    ...
    ...

然后你的类简化为:

require 'yaml'

class Countries
  include Singleton

  def get(country)
    @countries[country]
  end

  def initialize
    @countries = {}

    YAML.load_file('countries.yml').each do |country_key, options|
      country = Country.new(options['name'], options['tags'])
      country.gdp = options['gdp']
      country.population = options['population']

      @countries[country_key] = country
    end

    @countries.keys # just to keep the interface compatible with your example 
  end
end

【讨论】:

  • 谢谢!我考虑过使用 JSON 文件,但它似乎比它的价值更麻烦。
【解决方案2】:

有数百种方法可以干燥代码,但您的错误本质上不是使用hash data stucture(或建议的外部文件)。

我就是这样做的,我做了一些假设,希望对你有帮助!

# I'll assume that Country is an actual class with a purpose, and not a simple
# structure.
Country = Struct.new(:name, :tags, :gdp, :population)

# list of all the countries
COUNTRIES_TABLE = [
  ["Afghanistan", [:authoritarian, :reactionary], 20444, 26023],
  ["Albania", [:hybrid, :conservative], 13276, 2893]
  # etc..
]

COUNTRIES = COUNTRIES_TABLE.map { |e| Country.new(*e) }
# we could have directly defined the hash instead of a table, but this keeps
# everything more DRY
COUNTRIES_HASH = COUNTRIES.map {|e| [e.name, e]}.to_h

puts COUNTRIES_HASH["Albania"].name

COUNTRIES_HASH.map do |k,v|
  puts v.name
end

【讨论】:

  • 非常感谢您的帮助,尽管我认为从表中获取 3 个单独的对象到散列似乎有点多。不过,你知道得更多。
猜你喜欢
  • 2012-01-18
  • 2012-10-10
  • 2017-12-27
  • 1970-01-01
  • 2016-07-28
  • 2011-08-17
  • 1970-01-01
  • 2021-07-18
  • 1970-01-01
相关资源
最近更新 更多