Ruby中基于钩子的消息委托原理及两类实现疑问
关于《面向对象的Ruby》钩子方法设计的问题解答
针对Sandi Metz《面向对象的Ruby》6.5.2节中基类通过钩子消息解耦的设计,以下是两个问题的具体解答:
问题1:为何spares方法中对local_spares的调用会委托给子类实现?
这是由Ruby的方法查找机制决定的。当你创建RoadBike实例并调用spares时,虽然spares方法继承自父类Bicycle,但执行local_spares调用时,Ruby会从当前实例所属的类(RoadBike)开始向上查找方法:
- 首先在
RoadBike类中寻找local_spares方法,找到就直接使用子类的实现; - 只有当子类未定义该方法时,才会回溯到父类
Bicycle调用默认的空哈希实现。
你提到的“spares成为RoadBike的方法后优先在自身类查找”的思路是对的,本质是方法调用的上下文是子类实例,所以查找链从子类开始。
问题2:基类local_spares返回空哈希的目的是什么?能否改为抛出NotImplementedError?
返回空哈希的核心目的
基类返回空哈希是为了实现钩子方法的默认实现,主要带来两个好处:
- 基类可独立使用:直接实例化
Bicycle对象时,调用spares能正常返回基础备件的哈希,不会出现方法缺失的错误; - 降低子类耦合:子类可以选择是否重写
local_spares——如果子类没有额外备件需要添加,完全不需要编写该方法,依赖父类的默认实现即可,避免了强制子类实现不必要的方法,这正是书中强调的“降低耦合”的体现。
能否改为抛出NotImplementedError?
可以,但这会将设计从钩子方法模式转变为模板方法模式,两者的适用场景不同:
- 若改为抛错误,意味着强制所有子类必须实现
local_spares,否则调用spares时会报错。这种设计适合基类是抽象类、不允许直接实例化,且所有子类都必然有专属备件的场景; - 而原书中的设计更灵活:基类可单独使用,子类可选择性实现钩子方法,更符合松耦合的设计原则,避免了不必要的约束。
内容的提问来源于stack exchange,提问作者hitzoglin
相关产品推荐
相关产品推荐

