使用关键字 args 进行 ruby​​ 初始化

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)

Jör*_*tag 6

文字相当于将是这样:

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)

当然,这正是你所拥有的。这在行为上是等价的,但我认为它在意图上等价的,并且可以说更好、更正确。