在上一篇文章里我们看到调用对象的方法实质上是向对象发送消息下面我们再来看一个有趣的应用。在Ruby里字典可以通过 {key value} 来创建如果你使用的版本是1.9或以上当key的类型是Symbol时创建字典的语法可以进一步简化为 {key: value} 注意冒号要紧贴在key后面这使得我们可以创建这样的对象代码 1有没有觉得这个写法很面熟有些同学可能已经看出来了这个写法很像JSON事实上这个代码正是仿照Wikipedia上的JSON示例代码写出来的然而由于它本身是一个字典对象在访问里面的内容时需要使用字典的语法代码 2现在请思考一下有没有办法使它接受这样的做法呢代码 3有些同学可能已经反应过来了——打开Hash类重写method_missing方法代码 4当我们调用first_name和phone_numbers等不存在的方法时就会触发method_missing方法它首先检查字典是否包含这个方法名若是返回对应的值否则转交super处理。下面我们执行一下代码图 1很好基本上达到我们的预期了但是这种做法至少存在两个弊病第一它强制所有字典使用这种重定向逻辑第二如果字典的现有方法和它的键出现重名重定向逻辑将被忽略因为现有方法将被优先调用不会触发method_missing方法那么怎么解决这两个问题嗯……很抱歉这些问题不是今天的主角还是待到它们的主戏上演之时再行分解吧……现在请思考一个问题当我们调用一个方法时我们只需直呼其名像代码3的first_name和city等方法那样当然加上()也是可以的即person1.first_name()这意味着当我们引用一个方法的名字时我们实际上在引用它的返回值那么如果我想把一个方法本身而不是它的返回值作为参数传给另一个方法呢考虑这样一个情景我有一个购物车内有图书若干代码 5图书通过calc_preferential_price方法计算优惠价格代码 6现在我想创建一个check_out方法用来计算货款代码 7那么我该如何把calc_preferential_price方法传给check_out方法直接把方法的名字传给它肯定不行因为直呼其名意味着调用它得到的是它的返回值而不是它本身再说calc_preferential_price方法是有参数的仅仅直呼其名会引发ArgumentError异常那么怎样才能得到calc_preferential_price方法本身呢在回答这个问题之前我们得先搞清楚方法本身是什么它是以什么形式存在的有些同学可能猜出来了是对象。在Ruby里所有对象都有一个method方法这个说法其实不够准确但就目前而言你大可放心这样理解你可以通过它获取对象的实例方法的对象图 2从上图可以看到m1实质上是Method类的实例你可以通过它的call方法调用method1方法图 3然而calc_preferential_price方法并不在任何类里啊我们应该通过哪个对象的method方法来获取它的对象在上一篇文章里我曾经说过在Ruby里任意一个时刻都有一个默认对象那么此时的默认对象又是什么呢我们可以通过self来获知图 4从上图可以看到此时的默认对象是main对象它是Object类的实例事实上我们通常把这些游离方法称作顶层方法top-level method它们是以Object类的私有实例方法的形式存在的换句话说你可以在任何对象内部调用它们这个说法其实不够准确但就目前而言你大可放心这样理解图 5有了这些准备知识我们可以把代码7补充完整了代码 8值得提醒的是由于默认对象正是calc_preferential_price方法的所属对象于是我们可以省掉method方法前面的消息接收者当然你也可以自行加上。下面我们看看运行结果图 6对于实例方法我们可以通过method方法来获取它的对象那么类方法的对象又该如何获取呢比如说下面的Class1类的method3方法图 7我们知道Class1类是一个对象所以它也有method方法而method3是它的单例方法所以我们可以通过它的method方法获取method3方法的对象图 8现在请思考一下如果method3是一个顶层方法呢比如下面的top_lv_method2图 9它和没有self.前缀的顶层方法有没有什么区别它是Object类的私有实例方法还是私有类方法在回答这些问题之前我们得先搞清楚self是什么从图4可以看到self是main对象所以它是main对象的单例方法它和没有self.前缀的顶层方法之间的区别是它是main对象的公有方法并且