《Effective Ruby:改善Ruby程序的48条建议》一第7条:了解super的不同行为

简介:

本节书摘来自华章出版社《Effective Ruby:改善Ruby程序的48条建议》一书中的第2章,第2.2节,作者 [美]彼得 J.琼斯(Peter J. Jones),更多章节内容可以访问云栖社区“华章计算机”公众号查看

第7条:了解super的不同行为

假设你已经写好了一个类,这个类继承自一个基类。而被继承的类定义了一个不那么适合新类的方法。因此你决定改进那个方法,但是你不想完全替代既有方法,因为它承担了90%的必要且繁重工作。你也不想改变基类,因为那将破坏其他代码的功能。先忽略掉使用组合而非继承的这个更好的选项,让我们费点力气来重载这个方法。你最后做了这些改动:


9e8eee8d2f68a504d229cd9b7ddc40074a9a20a7


83a42823a65b5bee9b896bfb751c54b5f2ec03fc

你让自己陷入了困境。最大的问题是:你如何调用超类中的m1方法?如果你试图调用衍生类中的方法,你将陷入无限循环中。那没什么帮助。这就到了super出场的时候了:


2a21de3d06598613198015e2b3372d7149ce7878

现在我们有些眉目了。新版的m1方法使用super来调用父类的m1方法。本例中,super有点替代Base#m1的意思。你在能够直接使用Base#m1的任何地方都能使用super。这包括通过super传递你希望传递给Base类的m1的任何参数。(当然仅限于目标方法能够接收的那些参数。)小心这个陷阱。
即使super被用作并扮演得像个方法,它其实是Ruby的关键字。这之间的不同很重要,因为super是基于Ruby中设定为可选的元素——括号,来改变行为的。我说得很严肃哦,请仔细想上一秒。当使用super时,如果省略了括号是会改变其行为的。这比听上去还要严重。要知道括号改变了什么,我们需要回顾一下super的三种写法:
super的第一种用法最为平常——就是我们之前所见的那样。如果你至少给super一个参数,那么它的行为就好像常规的Ruby方法,并且括号是可选的。通过这种形式,super将参数全部传递给目标方法。
如果没有给super任何参数和括号,它的行为可能会和你的预期不同。这种用法之下,super在调用目标方法时将宿主方法(enclosing method)的所有参数全部传递过去。如果存在方法块它也会一并传递。
你应该可以看出,这种形式下的super会带来一些副作用。首先,这样使用super仅仅在目标方法和宿主方法接收相同数量的参数时才可用。比如,这对我们之前看到的例子中的方法Base#m1和Derived#m1就不适用。试图在Derived#m1方法中使用无参的super会引发一个ArgumentError异常。如果被重载的方法不接收参数而宿主方法接收,那么着同样会为你带来一个相同的异常。
可以看出的第二件事与传递给重载方法的参数的值有关。如果你在使用无参形式调用super之前,在宿主方法中改变了其参数中的一个值,那么super会将改变后的当前值传递给目标方法,而非原始的值。这看起来非常合理,但有时你也需要注意。
当你希望在使用super时不将任何参数传递给重载方法,你需要使用空括号,即super()。在Ruby中这种形式看起来实在很怪。即使我们喜欢使用括号,在无参调用时也不会加上它们。这种super的用法看起来有些不自然,但这是无参(也没有块)调用重载方法的唯一途径。
以下是对上述规则的代码表述:
image

下一个需要关心的是super如何寻找要重载的方法。你也许会简单地想,只要调用其超类的同一方法就可以了,但这也简单地过分了哦。事实上,super是从整个继承体系中寻找的,见第6条,不过有些许微小的差异。使用super时,它从继承体系的上一层去寻找和当前方法同名的方法。所以,它将从当前类的上一层开始寻找。
如果你还记得我们对继承体系的研究,你将明白上一层可能会是一个单例类。这很棒啊,因为这意味着super能够对其引用的模块中的方法进行重载。
image

这个例子也指出了使用super的一个限制。如果你希望调用一个定义在超类中的方法,而与此同时如果包含的模块中也定义了同名方法,super就无法帮你达到目的。显然,super会在找到第一个匹配的同名方法后停下来,而那是包含的模块中的方法,而不是超类中的。但如果你真的意外遇到了这种情况,那么可能在设计上存在严重的问题。可以考虑使用组合而非继承了。
最后一个需要警示的是super是如何与method_missing相互作用的。如果你思考过这个问题,你会知道,只要在继承体系中存在另一个同名方法,使用super就不会引发method_missing。另一方面,如果你恰巧是一个瞌睡的开发者或者正在使用一些元编程的黑魔法,你最终可能会在使用super时引发method_missing。如果是这样,你读到这节会非常开心。
就像一个普通的方法查找一样,如果super无法找到它要寻找的方法,将调用method_missing来报告这个错误。如你料想的那样,Ruby寻找method_missing时会从原接收者所在的类开始查找。这一切都很好,我们还可以从异常结果中得到有用的错误信息。
image

Ruby会在内部追踪method_missing方法是否由一个错误的super调用引发,若是如此,会在异常中给出一些额外的信息。但这仅发生在Ruby没能在继承体系中找到已定义的method_missing方法而使用默认实现时。一旦继承体系中存在任何定义了method_missing方法的类,你就会丢失这个有用的细节。这个信息无法找回,即使你在自己的method_missing实现中调用super也不行。
再多说点,method_missing在被调用时描述的细节是说,方法是存在的,只是它在错误的地方。如果上面的SuperHappy类定义了method_missing方法,它将在调用laugh时被调用,因为那个方法试图使用super调用另一个不存在的laugh方法。那么method_missing的第一个参数是什么呢?对的,就是缺少的方法的名字,本例中就是:laugh。但是SuperHappy本身是有laugh方法的。有点迷茫了是不是?
假如你想细数避免自行定义method_missing方法的原因,请确保你也算上了这个(包括第30条里提到的)。本例中,来自method_missing方法的默认实现中的NoMethodError异常显然比我们自己定义的method_missing方法更好。
要点回顾
当你想重载继承体系中的一个方法时,关键字super可以帮你调用它。
不加括号地无参调用super等价于将宿主方法的所有参数传递给要调用的方法。
如果希望使用super并且不向重载方法传递任何参数,必须使用空括号,即super()。
当super调用失败时,自定义的method_missing方法将丢弃一些有用的信息。在第30条中有method_missing的替代解决方案。

相关文章
|
缓存 监控 数据库
使用Ruby构建可扩展的Web应用程序
在当今科技驱动的世界中,Web应用程序成为了企业和个人进行业务活动、提供服务和与用户互动的重要方式。而Ruby作为一种简洁、优雅且易于学习的编程语言,已经成为许多开发者的选择。本篇博客将介绍如何使用Ruby构建可扩展的Web应用程序。
101 0
Ruby Programming | 连载 07 - Ruby 对象的原生行为
Ruby Programming | 连载 07 - Ruby 对象的原生行为
Ruby Programming | 连载 07 - Ruby 对象的原生行为
|
测试技术 C++ Ruby
C++程序中嵌入Ruby脚本系统
Ruby,一种为简单快捷面向对象编程(面向对象程序设计)而创的脚本语言,由日本人松本行弘(まつもとゆきひろ,英译:Yukihiro Matsumoto,外号matz)开发,遵守GPL协议和Ruby License。
1609 0
|
消息中间件 安全 Ruby
Nanite:Ruby程序的一个自我装配集群
本文讲的是Nanite:Ruby程序的一个自我装配集群,Nanite(由Ezra Zygmuntowicz开发)是Engine Yard云计算策略的一个新兵:它是“Ruby程序的一个自我装配集群”,用以构筑高度可伸缩的Web应用的后端。
1121 0
|
程序员 Ruby
《Effective Ruby:改善Ruby程序的48条建议》一导读
学习一门新的编程语言通常需要经过两个阶段。第一阶段是学习这门编程语言的语法和结构。如果我们具有其他编程语言的经验,这个阶段通常只需要很短的时间。以Ruby为例,接触过其他面向对象语言的程序员对Ruby的语法也会比较熟悉。有经验的程序员对于语言的结构(如何根据语法构建应用程序)是很熟悉的。
1234 0
|
C语言 C++ Ruby
编译c语言程序扩展ruby
环境: windows 10 64bit ruby 2.2.4p230 (2015-12-16 revision 53155) [i386-mingw32] gcc version 4.8.
1063 0
|
Web App开发 Ruby
第一个Ruby On Rails WEB应用程序HelloWorld
经过不懈努力终于把Ruby On Ralis 的开发环境搭建好了,在WIN7系统下,赶紧来个最最常用的例子来练练手吧,感受一下Ruby。。。 再次声明一下,我的ruby -v是ruby-2.0.0p247。
1290 0
|
NoSQL 数据库 Ruby
我的第一个Ruby On Rails + MongoDB程序
    最近想进一步学习一下MongoDB,而很久之前使用过ROR,正好也凑个机会重新拾起来。下面是建立第一个项目的过程。        主要参考文档:        1. Rails 3 - Getting started        2.
954 0
|
Apache Ruby Windows
让Apache支持ruby写的cgi程序
ruby apache cgi 配置 1,按说明安装ruby的apache mod 2,配置文件如下: 1 ######################################################### 2 # ruby cgi 配置 3 #######...
777 0