我正在尝试将对象集合存储在另一个对象中。我完成了一个类似于以下的编码挑战。
首先是单个对象:
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
以获取帐户对象列表。这有点奇怪吗?任何人都可以告诉我如何做得更好吗?
答案 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
将会非常高兴他们可以访问他们使用的所有搜索和遍历方法到。