存储一组Ruby对象

时间:2018-06-06 14:59:24

标签: ruby oop

我正在尝试将对象集合存储在另一个对象中。我完成了一个类似于以下的编码挑战。

首先是单个对象:

class Account
  attr_reader :user_name, :credit, :debit

  def initialize(user_name)
    @user_name = user_name
    @credit = 0
    @debit = 0
  end
end

接下来的收藏:

class AccountsCollection
  attr_reader :accounts

  def initialize
    @accounts = []
  end

  def add_new_account(user)
    accounts << Account.new(user)
  end

  ...
end

这就是我使用它的方式:

accounts = AccountsCollection.new
# => #<AccountsCollection:0x00007fc76ba70b18 @accounts=[]>
accounts.add_new_account('A')
accounts.add_new_account('B')
accounts.add_new_account('C')
accounts.accounts
# =>[
#     #<Account:0x00007fc76b933890 @user_name="A">,
#     #<Account:0x00007fc76bc76d68 @user_name="B">,
#     #<Account:0x00007fc76c88c2d8 @user_name="C">
#   ]

我想这样使用它:

class Display
  attr_reader :accounts

  def initialize(accounts)
    @accounts = accounts
  end

  def display_inline
    accounts.each do |account|
      #do something
  end
  ...
end

Display.new(accounts.accounts).display_inline

但我必须致电accounts.accounts以获取帐户对象列表。这有点奇怪吗?任何人都可以告诉我如何做得更好吗?

2 个答案:

答案 0 :(得分:2)

除了命名看起来很尴尬之外,对我来说真的很好看。如果是我,我会有这样的名字,所以看起来很好用。

class AccountsCollection
  def initialize
    @accounts = []
  end

  def add(user)
    accounts << Account.new(user)
  end

  def to_a
    @accounts
  end
end

然后你的代码看起来像

accounts = AccountsCollection.new
 => #<AccountsCollection:0x00007fc76ba70b18 @accounts=[]>

accounts.add('A')
accounts.add('B')
accounts.add('C')

accounts.to_a
 =>[
   #<Account:0x00007fc76b933890 @user_name="A">,
   #<Account:0x00007fc76bc76d68 @user_name="B">,
   #<Account:0x00007fc76c88c2d8 @user_name="C">
 ]

答案 1 :(得分:0)

代码味道,IMO,你的代码只是它是一个集合,但我不能把它当成一个集合。例如,如果我想要拥有超过10个学分的所有帐户,我将不得不从该集合中拉出帐户数组来执行此操作:

accounts.accounts.select { |account| account.credit > 10 }

而你真正想要的是能够只为它查询集合:

accounts.select { |account| account.credit > 10 }

要使您的收藏更像集合,您需要添加Enumerable,这需要您实施each方法:

class AccountsCollection
  include Enumerable

  def initialize
    @accounts = []
  end

  def add(user_name)
    @accounts << Account.new(user_name)

    self # return self because that's more in-line with other collections
  end

  def each
    return to_enum(__method__) { @accounts.size } unless block_given?

    @accounts.each { |account| yield account }
  end
end

只是你现在可以像对待ruby中的其他集合一样对待你的集合:

accounts = AccountsCollection.new
accounts.add('A').add('B').add('C')

accounts.each.with_index do |account, index|
  # I changed the attr_reader in Account to attr_accessor just to illustrate
  account.credit = index * 10
end

accounts.select { |account| account.credit > 10 }
# => [#<Account:0x00007fe50d894208 @user_name="C", @credit=20, @debit=0>]

如果你想把它作为一个数组,你自动得到一个to_a方法:

accounts.to_a
# => [#<Account:0x00007f879a094538 @user_name="A", @credit=0, @debit=0>,
#     #<Account:0x00007f879a094308 @user_name="B", @credit=10, @debit=0>,
#     #<Account:0x00007f879a0968b0 @user_name="C", @credit=20, @debit=0>]

这个(默认情况下)将通过each方法创建一个新数组,因此不会直接修改数组(尽管内容仍然可以)

accounts.to_a[1].debit = 15
accounts.to_a << 'not an account'

accounts.to_a
# => [#<Account:0x00007fb51c08a9c0 @user_name="A", @credit=0, @debit=0>,
#     #<Account:0x00007fb51c08a6a0 @user_name="B", @credit=10, @debit=15>,
#     #<Account:0x00007fb51c08a588 @user_name="C", @credit=20, @debit=0>]

如果您愿意,可以随时使用更好的自定义实现替换Enumerable的任何方法:

def to_a
  @accounts.dup
end

现在事情已开始变得更好,感觉更好,工作量极少,而其他人试图使用你的AccountsCollection将会非常高兴他们可以访问他们使用的所有搜索和遍历方法到。