Ruby子类实例变量别名实现问题:如何为实例变量设置别名
问题原因先搞清楚!
你现在的代码里,alias :foo :bar是给实例方法bar(也就是attr_reader生成的读取方法)起了别名,而不是给实例变量@bar创建别名@foo。Ruby的实例变量是直接通过@+名字来引用的,语言本身并没有提供实例变量别名的机制,所以直接访问@foo自然会得到nil——因为这个变量从来没被赋值过。
几种解决方案
方案1:用方法别名代替实例变量别名(最简单)
既然你已经给bar方法起了别名foo,那直接在子类里调用foo方法就行,完全没必要直接访问实例变量:
class A attr_reader :bar alias :foo :bar def initialize @bar = 'hello' end end class B < A def try_alias puts foo # 调用foo方法,本质就是调用bar方法返回@bar的值 end end B.new.try_alias #=> "hello"
这种方式最符合Ruby的设计习惯,因为直接访问实例变量通常不是最佳实践,用访问器方法更易维护。
方案2:同步实例变量的值(如果你一定要用@foo)
如果确实需要在实例变量层面让@foo和@bar保持同步,那只能在修改@bar的时候手动同步@foo。最优雅的方式是通过自定义setter方法来实现:
class A # 只定义读取方法,写入用自定义setter attr_reader :bar, :foo def bar=(value) @bar = value @foo = value # 同步赋值给@foo end def initialize self.bar = 'hello' # 这里必须用self.bar=,不然会被当成局部变量赋值 end end class B < A def try_alias puts @foo # 现在@foo和@bar的值一致了 end end B.new.try_alias #=> "hello"
如果后续需要修改bar的值,只要调用obj.bar = 'new value',@foo也会自动同步更新。
方案3:动态获取(不推荐)
还有一种更hack的方式,就是通过instance_variable_get来模拟“别名”,但这种方式可读性差,不建议在生产代码里用:
class A attr_reader :bar def initialize @bar = 'hello' end end class B < A def try_alias # 直接通过名字获取@bar的值,模拟别名效果 puts instance_variable_get(:@bar) # 或者在子类里定义foo方法映射到@bar def foo @bar end puts foo # 这样能拿到值,但@foo还是nil,本质还是方法访问 end end
这种方式本质还是绕回了方法访问,所以不如直接用方案1。
总结
- 优先用方法别名(方案1),这是Ruby推荐的做法,符合面向对象的封装原则;
- 如果必须要实例变量层面的同步,那就用自定义setter来维护两个变量的一致性(方案2);
- Ruby本身没有内置的实例变量别名机制,只能通过上述方式模拟实现需求。
内容的提问来源于stack exchange,提问作者Václav Pruner
相关产品推荐
相关产品推荐

