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 的回答,因为它看起来更安全
我目前正在使用 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 的回答,因为它看起来更安全