Ruby 案例陈述的替代方案

Alternative to Ruby Case statement

我目前正在使用 Ruby 为银行管理系统编写程序。该系统的一个功能是可以创建一个新帐户,帐户可以是六种类型之一。

我的控制器中有以下方法来满足此功能:

def create_account(type, holder)
  case type
  when :current  then CurrentAccount.new(holder, @account_number)
  when :savings  then SavingsAccount.new(holder, @account_number)
  when :business then BusinessAccount.new(holder, @account_number)
  when :ir       then IRAccount.new(holder, @account_number)
  when :smb      then SMBAccount.new(holder, @account_number)
  when :student  then StudentAccount.new(holder, @account_number)
  end
end

这些帐户中的每一个都继承自一个基本帐户,最终将包含单独的属性,例如利率、透支等

虽然这很实用并且提供了所需的结果,但感觉有点冗长。但是我想不出任何明显的重构方法。

欢迎提出任何建议...

我假设系统或最终用户在某些时候有效地选择了文本类型,您需要将其转换为 class 才能使用。否则,您可以编写简单引用并实例化正确 class.

的调用代码

您可以通过定义符号 type 和 class 之间的映射来使您拥有的内容更清晰。所以你可以在 create_account:

的范围内这样做
ACCOUNT_CLASS_FOR = Hash[
  current:  CurrentAccount,
  savings:  SavingsAccount,
  business: BusinessAccount,
  ir:       IRAccount,
  smb:      SMBAccount,
  student:  StudentAccount
]

def create_account(type, holder)
  if account_class = ACCOUNT_CLASS_FOR[ type ]
    account_class.new( holder, @account_number )
  else
    raise "Bad account type #{type}"
  end
end

这样重复代码少,符号名与匹配Rubyclass的映射更加明确。如果您需要在其他地方应用或测试转换,您可以使常量在不同的范围内可用,而无需自己重复。

您可以让每个 class 知道自己的标签,例如

class CurrentAccount
  def self.label
     :current
  end
end

那么你可以有这样的东西:

ALLOWED_ACCOUNT_CLASSES = [CurrentAccount,SavingsAccount,BusinessAccount, # etc.

ACCOUNT_CLASS_FOR = Hash[
  ALLOWED_ACCOUNT_CLASSES.map { |klass| [klass.label, klass] }
]

请注意,在这里使用拼写错误的 klass 变量是很常见的做法,以避免与 Ruby 的 class 关键字发生冲突,但您也可以只使用 account_class

这是另一种方法,但是您需要使用 class 相应地命名类型(即 :ir -> :i_r )

def create_account(type, holder)
    Object.const_get(type.to_s.camelize + "Account").new(holder, @account_number)
end

即使这个更短,我也喜欢 Neil 的回答,因为它看起来更安全