使用当前对象访问不同类的实例方法

huangapple go评论64阅读模式
英文:

Accessing instance method of a different class with current object

问题

以下是翻译好的部分:

从书籍《Ruby实战面向对象设计》(Practical Object-Oriented Design in Ruby),作者是Sandi Metz:

class Gear
  attr_reader :chainring, :cog, :rim, :tire
  def initialize(chainring, cog, rim, tire)
    @chainring = chainring
    @cog = cog
    @rim = rim
    @tire = tire
  end

  def ratio
    chainring / cog.to_f
  end

  def gear_inches
    ratio * Wheel.new(rim, tire).diameter #line number 11
  end
  # ...
end

class Wheel
  attr_reader :rim, :tire
  def initialize(rim, tire)
    @rim = rim
    @tire = tire
  end

  def diameter
    rim + (tire * 2)
  end
  # ...
end

Gear.new(52, 11, 26, 1.5).gear_inches

这个引用的直接、明显的后果是,如果Wheel类的名称发生变化,那么Gear的gear_inches方法也必须发生变化。
从表面上看,这种依赖关系似乎是无害的。毕竟,如果Gear需要与Wheel交互,那么在某个地方必须创建Wheel类的新实例。如果Gear本身知道Wheel类的名称,那么如果Wheel的名称发生变化,必须修改Gear中的代码。

事实上,处理名称更改是一个相对较小的问题。您可能有一个工具可以在项目中进行全局查找/替换。如果Wheel的名称更改为Wheely,查找和修复所有引用并不是很困难。然而,与此代码的问题相比,上面的第11行必须在Wheel类的名称更改时发生变化只是其中的一个问题。存在一个更深层次的问题,它远不那么显而易见,但危害更大。

当Gear在其gear_inches方法内部硬编码对Wheel的引用时,它明确地声明,它只愿意为Wheel的实例计算齿轮英寸。Gear拒绝与任何其他类型的对象合作,即使该对象具有直径并使用齿轮。

如果您的应用程序扩展到包括诸如磁盘或圆柱体之类的对象,并且您需要知道使用它们的齿轮的齿轮英寸,那是不可能的。尽管磁盘和圆柱体自然具有直径,但您永远无法计算它们的齿轮英寸,因为Gear被限定在Wheel上。

关于您的问题,如何让磁盘和圆柱体对象访问gear_inches方法,因为gear_inches是Gear类的实例方法(向磁盘和圆柱体发送gear_inches消息),答案是它们不能直接访问它,因为Gear类硬编码了对Wheel类的依赖。如果您想让磁盘和圆柱体对象能够计算齿轮英寸,您需要重构代码,以便Gear类不依赖于特定的类(例如Wheel)。这可以通过使用依赖注入、接口或多态等设计原则来实现,以使Gear能够与不同类型的对象合作,只要它们具有必要的属性和方法。

英文:

From the book Practical Object-Oriented Design in Ruby, by Sandi Metz:

class Gear
 attr_reader :chainring, :cog, :rim, :tire
 def initialize(chainring, cog, rim, tire)
  @chainring = chainring
  @cog = cog
  @rim = rim
  @tire = tire
 end

  def ratio
   chainring / cog.to_f
  end    
 def gear_inches
  ratio * Wheel.new(rim, tire).diameter #line number 11
 end
 # ...
end

class Wheel
 attr_reader :rim, :tire
 def initialize(rim, tire)
  @rim = rim
  @tire = tire
 end

 def diameter
  rim + (tire * 2)
 end
 # ...
 end

 Gear.new(52, 11, 26, 1.5).gear_inches

> The immediate, obvious consequence of this reference is that if the name of the
Wheel class changes, Gear’s gear_inches method must also change.
On the face of it this dependency seems innocuous. After all, if a Gear needs to
talk to a Wheel, something, somewhere, must create a new instance of the Wheel
class. If Gear itself knows the name of the Wheel class, the code in Gear must be
altered if Wheel’s name changes.

>In truth, dealing with the name change is a relatively minor issue. You likely have
a tool that allows you to do a global find/replace within a project. If Wheel’s name
changes to Wheely, finding and fixing all of the references isn’t that hard. However,
the fact that line 11 above must change if the name of the Wheel class changes is the
least of the problems with this code. A deeper problem exists that is far less visible but
significantly more destructive.

>When Gear hard-codes a reference to Wheel deep inside its gear_inches
method, it is explicitly declaring that it is only willing to calculate gear inches for
instances of Wheel. Gear refuses to collaborate with any other kind of object, even if
that object has a diameter and uses gears.

>If your application expands to include objects such as disks or cylinders and you
need to know the gear inches of gears which use them, you cannot. Despite the fact
that disks and cylinders naturally have a diameter you can never calculate their gear
inches because Gear is stuck to Wheel.

I couldn't get wrap my head around the paragraph which is made bold. How can disk and cylinder objects access the method gear_inches, since gear_inches is an instance method of class Gear.(Sending gear_inches message to Disk and Cylinder)

Kindly clarify.

答案1

得分: 1

将其视为更加假设性的情景,因为它描述的内容在你提供的示例中显然没有编码。

首先,请注意,在这种情况下,Wheel 实例没有访问 Gear 类中的任何方法。相反,Gear 正在访问具有 diameter 方法的 Wheel 实例。

其次,想象一下,如果 Gear 需要为可能不同类型的对象计算直径,那么现在使用 Wheel.new(...) 就没有任何意义了!你可以这样做,将具有 diameter 方法的对象作为参数传递,并在 gear_inches 内部调用该方法:

# 一种可能的实现
def gear_inches(object_with_diameter)
  ratio * object_with_diameter.diameter
end

现在,gear_inches 取决于传递的对象类型,而不是与 Wheel 绑定在一起。

英文:

Think of it as a more hypothetical scenario, since what it describes is clearly not coded in the example you provided.

First, note that the Wheel instance isn't accessing any methods in the Gear class in this case. The other way around, Gear is accessing methods from a Wheel instance.

Second, think how a Gear would calculate a diameter for a potentially different type. Now using Wheel.new(...) wouldn't make any sense! What you could do, is pass the object that has a diameter method as a parameter, and call that method inside of gear_inches:

# One possible implementation
def gear_inches(object_with_diameter)
  ratio * object_with_diameter.diameter
end

Now the gear_inches depend on what type of object is being passed, and isn't tied to Wheel.

答案2

得分: 1

如何让磁盘和圆柱对象访问gear_inches方法,因为gear_inchesGear类的实例方法。

文本没有说磁盘或圆柱应该调用gear_inches。恰恰相反,gear_inches需要一个_直径_来计算其返回值。现在,它从自己创建的Wheel实例获取直径。

然而,将Wheel.new(...)硬编码到方法中会在GearWheel之间创建耦合关系。为了减少这种耦合,你可以将轮子实例_传递_给Gear的初始化器。这正是Sandi在下一个示例中所展示的:

POODR,第41页:

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

  def gear_inches
    ratio * wheel.diameter
  end
  # ...
end

# Gear期望一个“鸭子”,它知道“直径”
Gear.new(52, 11, Wheel.new(26, 1.5)).gear_inches

"一个 '鸭子',它知道 '直径'" 意味着wheel不必是一个真正的轮子(Wheel类的实例)。相反,它可以是任何响应diameter的对象,例如一个(假设的)磁盘或圆柱。

英文:

> How can disk and cylinder objects access the method gear_inches, since gear_inches is an instance method of class Gear.

The text doesn't say that disk or cylinder are supposed to call gear_inches. Quite the opposite: gear_inches needs a diameter to calculate its return value. Right now, it gets that diameter from a Wheel instance that it creates on its own.

However, hard-coding Wheel.new(...) into the method creates a coupling between Gear and Wheel. To loosen this coupling, you could for example pass the wheel instance to Gear's initializer. Which is exactly what Sandi shows in the next example:

> POODR, page 41:
>
> class Gear
> attr_reader :chainring, :cog, :wheel
> def initialize(chainring, cog, wheel)
> @chainring = chainring
> @cog = cog
> @wheel = wheel
> end
>
> def gear_inches
> ratio * wheel.diameter
> end
> # ...
> end
>
> # Gear expects a 'Duck' that knows 'diameter'
> Gear.new(52, 11, Wheel.new(26, 1.5)).gear_inches
>

"a 'Duck' that knows 'diameter'" means that wheel doesn't have to be an actual wheel (an instance of the Wheel class). Instead, it could be any object that responds to diameter, like for example a (hypothetical) disk or cylinder.

huangapple
  • 本文由 发表于 2023年7月20日 22:39:08
  • 转载请务必保留本文链接:https://go.coder-hub.com/76731010.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定