关于Gear类gear_inches方法硬编码依赖Wheel类的扩展性问题及相关疑问
嘿,我来帮你把这个困惑捋明白~
首先你可能误解了这里的场景:不是要让Disk或者Cylinder去调用Gear的gear_inches方法,而是希望同一个Gear实例可以和不同的、具备直径属性的部件(比如Disk、Cylinder)协作,计算出对应的齿轮英寸值。原代码的问题就出在Gear把自己和Wheel绑定得太死了,导致它没办法和其他部件配合。
咱们先看原代码的核心问题:
在Gear的gear_inches方法里,它直接硬编码创建了Wheel.new(rim, tire)实例,这相当于Gear在说:“我只跟Wheel这个类合作,其他玩意儿一概不认”。哪怕Disk或者Cylinder这类对象天生就有diameter方法,Gear也完全没办法和它们协作——因为它在代码里写死了必须用Wheel类。
举个实际场景:假设你现在要做一个机械模拟程序,里面除了带轮胎的车轮,还有实心圆盘(Disk)、圆柱滚轮(Cylinder),这些部件都有直径,都能和齿轮配合计算齿轮英寸。但原代码里的Gear,不管你给它传的是Disk还是Cylinder的参数,它都会在gear_inches里强行创建一个Wheel实例,根本不会用你给的其他部件,自然算不出正确的结果。
那怎么解决这个问题?核心思路是把依赖的对象“注入”给Gear,而不是让Gear自己创建。比如修改Gear的设计,让它初始化时接收一个具备diameter方法的对象(不管是Wheel、Disk还是别的),这样Gear就只关心这个对象有没有diameter方法,而不关心它具体是什么类。
修改后的代码大概是这样:
class Gear attr_reader :chainring, :cog, :rotor # 初始化时接收一个带diameter方法的部件对象 def initialize(chainring, cog, rotor) @chainring = chainring @cog = cog @rotor = rotor end def ratio chainring / cog.to_f end def gear_inches # 直接用传入的rotor的diameter,不再硬编码创建Wheel ratio * rotor.diameter end end # 现在可以随便加其他带diameter的类 class Disk attr_reader :diameter def initialize(diameter) @diameter = diameter end end class Cylinder attr_reader :radius def initialize(radius) @radius = radius end def diameter radius * 2 end end # 使用示例:和Wheel配合 gear_with_wheel = Gear.new(52, 11, Wheel.new(26, 1.5)) puts gear_with_wheel.gear_inches # 输出和原代码一样的结果 # 和Disk配合 gear_with_disk = Gear.new(52, 11, Disk.new(30)) puts gear_with_disk.gear_inches # 正常计算齿轮英寸 # 和Cylinder配合 gear_with_cylinder = Gear.new(52, 11, Cylinder.new(15)) puts gear_with_cylinder.gear_inches # 同样能计算
这样一来,Gear就摆脱了对Wheel类的硬编码依赖,只要是有diameter方法的对象,它都能愉快协作——这就是面向对象里“依赖倒置”和“鸭子类型”的好处,代码的扩展性一下子就上来了。
备注:内容来源于stack exchange,提问作者current_user

