Sea*_*yar 2 ruby constructor initialization keyword-argument
我正在阅读 POODR 书,它使用旧语法进行默认值初始化。我想用新语法实现相同的功能。
class Gear
attr_reader :chainring, :cog, :wheel
def initialize(args)
@chainring = args.fetch(:chainring, 40)
@cog = args.fetch(:cog, 10)
@wheel = args[:wheel]
end
def gear_inches
ratio * diameter
end
def diameter
wheel * diameter
end
end
Gear.new(chainring: 52, cog: 11, wheel: Wheel.new(26,1.5)).gear_inches
Run Code Online (Sandbox Code Playgroud)
使用新的关键字 args 会是什么样子?这是我在下面的猜测,但不确定它是否与上述车轮的结果相同。
class Gear
attr_reader :chainring, :cog, :wheel
def initialize(chainring: 40, cog: 10, wheel:) #is this good here for wheel?
@chainring = chainring
@cog = cog
@wheel = wheel #is this good here for wheel?
end
......
end
Run Code Online (Sandbox Code Playgroud)
该文字相当于将是这样:
class Gear
def initialize(**args)
@chainring = args.fetch(:chainring, 40)
@cog = args.fetch(:cog, 10)
@wheel = args[:wheel]
end
end
Run Code Online (Sandbox Code Playgroud)
原始代码允许传递任意键,并忽略不需要的键,因此,我们使用 **ksplat 来允许任意参数。
我们可以将该代码重构为:
class Gear
def initialize(chainring: 40, cog: 10, **args)
@chainring = chainring
@cog = cog
@wheel = args[:wheel]
end
end
Run Code Online (Sandbox Code Playgroud)
这读起来稍微好一点。但它仍然是糟糕的设计:为什么允许用户传递任意键?当传递未使用的密钥时,它很可能是一个错误。例如,用户调用Gear.new(cgo: 20),这显然是一个错字,但他不会得到错误,而是会默默地得到错误的数据(cog值为10)。
class Gear
def initialize(chainring: 40, cog: 10, wheel: nil)
@chainring = chainring
@cog = cog
@wheel = wheel
end
end
Run Code Online (Sandbox Code Playgroud)
我猜这相当于原始代码的预期行为。它的行为不同,因为它不允许传递任意键,但我认为这没有任何意义。因此,虽然不等价,但可以说是更好。
但是,仍然存在一个问题(原代码中也存在):有可能不通过wheel,使wheelend up nil。但是,wheel是无条件使用的(例如 in diameter),这意味着它会在运行时在wheelis时爆炸nil。因此,最好要求wheel通过:
class Gear
def initialize(chainring: 40, cog: 10, wheel:)
@chainring = chainring
@cog = cog
@wheel = wheel
end
end
Run Code Online (Sandbox Code Playgroud)
当然,这正是你所拥有的。这在行为上是不等价的,但我认为它在意图上是等价的,并且可以说更好、更正确。