用关键字args初始化ruby

时间:2016-06-01 10:35:48

标签: 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

使用new关键字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

2 个答案:

答案 0 :(得分:2)

literal 等价物将是:

class Gear
  def initialize(**args)
    @chainring = args.fetch(:chainring, 40)
    @cog =  args.fetch(:cog, 10)
    @wheel = args[:wheel]
  end
end

原始代码允许传递任意键,只是忽略它不需要的键,因此,我们使用** ksplat来允许任意参数。

我们可以将代码重构为:

class Gear
  def initialize(chainring: 40, cog: 10, **args)
    @chainring = chainring
    @cog = cog
    @wheel = args[:wheel]
  end
end

这读得稍微好一点。但它仍然是糟糕的设计:为什么允许用户传递任意键?当未使用的密钥通过时,它很可能是一个错误。例如。用户调用Gear.new(cgo: 20),这显然是一个错字,但他没有收到错误,而是默默地收到错误的数据(cog 10}。

class Gear
  def initialize(chainring: 40, cog: 10, wheel: nil)
    @chainring = chainring
    @cog = cog
    @wheel = wheel
  end
end

这相当于原始代码的预期行为,我猜。它的行为有所不同,因为它不允许传递任意键,但我认为这无论如何都没有意义。所以,虽然不等同,但可以说是更好

但是,仍有问题(原始代码中也存在):可能无法通过wheel,使wheel结束nil。但是,无条件地使用wheel(例如diameter),这意味着,wheelnil时,它会在运行时爆炸。因此,最好要求wheel传递:

class Gear
  def initialize(chainring: 40, cog: 10, wheel:)
    @chainring = chainring
    @cog = cog
    @wheel = wheel
  end
end

当然,这正是你所拥有的。这 not 在行为上是等效的,但我认为 等同于 intent ,可以说它更好更正确。

答案 1 :(得分:0)

您的带有关键字参数的示例与旧方法不同,因为将需要:wheel,而在旧代码中则不是。

如果您希望关键字是可选的,则无法使用关键字参数。