英文:
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_inches
是Gear
类的实例方法。
文本没有说磁盘或圆柱应该调用gear_inches
。恰恰相反,gear_inches
需要一个_直径_来计算其返回值。现在,它从自己创建的Wheel
实例获取直径。
然而,将Wheel.new(...)
硬编码到方法中会在Gear
和Wheel
之间创建耦合关系。为了减少这种耦合,你可以将轮子实例_传递_给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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论