转入魔都
啊哈~自从不做阿里实习生之后,在学校浑浑噩噩过了一周左右,终于又开始找实习~先投了猿题库,无奈要等到十月中旬才能面试,就先面了eBay,没想到还挺顺利地就过了,就这么当着杭州的二房东跑到上海开始实习生涯了~~ P.S. 6号拿到offer13号入职,到现在才写下这段话的效率也是堪忧啊~
初遇Ruby
说是Ruby元编程,是打算写下读《Ruby元编程》的一些笔记,但是在这之前,还是要先讲讲怎么会开始学习Ruby了呢~
10月13日,那是一个风和日丽的早晨。来到eBay之前就风闻是个很闲适的公司,闲适到七爷得知我到eBay实习后的第一回应就是“哦!很清闲!”。当主管和我聊到需要做的事情的时候,才知道需要使用Ruby开发,说实话心里还有点小激动“早听说Ruby很厉害,我要成为那个不拘一格的一员了么~”。其实一般都会有这样的心态:使用越小众的语言就显得越厉害…当然残存的理智告诉我,程序员是不可能因为换个语言而变厉害的,更何况一个连Java都谈不上精通的程序员。
学之前还是挺忐忑的,但是我那慵懒的,十二点左右上班,六点左右下班的mentor跟我说“这玩意儿很简单的,你随便看看就会了”。于是我收拾心情,花了一个晚上看了一下Ruby语法,诶~感觉跟python还蛮像的,可能还真的很简单呢!
就这样,我要开始我的Ruby学习之旅了~!
上面这样的句式总是会让人感觉你现在已经是某某的大师,其实我现在还什么都不懂哈哈!
Ruby元编程
好了,终于开始正篇。
其实一个语言的语法很简单,有一本书大致叫《七天学习七门语言》,讲的就是七天内掌握七门语言的语法,这对于熟悉一门编程语言的人来说其实并不难。但语言的精髓其实在于它的设计思想,当真正领会了一门语言的设计思想之后,我想以后学习其他语言的时候,不仅能很快掌握语法,更能迅速地利用这门语言的特性写出优秀的代码吧。所以在看了“Ruby菜鸟教程”这种语法科普之后,我就开始看《Ruby元编程》啦,因为是松本行弘本人写的,看网上的评价也非常好,所以…希望能从中一窥Ruby号称的“元编程”到底比起Java等语言神奇在何处。
《Ruby元编程》其实是一个故事…书中主人公跟我一样是一个刚进公司的实习生,和他结对编程兼任mentor的是一个Ruby大师~然后他们的故事以一天为一章…其实书很薄,当然也要看有没有足够的时间,所以~就按照章节来写笔记吧。
今天是“星期一”。
内省
在很多语言,比如C++中,语言构件(变量、类、方法等)在编译器完成工作之后,就变成了看不见摸不着的东西,只是内存位置而已。
而在Ruby和其他很多语言中,运行时绝大多数的语言构件依然存在。可以从构件处获得关于它自己的信息。这称为内省。
内省的例子:
class Greeting
def initialize(text)
@text = text
end
def welcome
@text
end
end
my_obj = Greeting.new("Hello")
puts my_obj.class ,my_obj.class.instance_methods(false) ,my_obj.instance_variables
通过这样的代码,就可以获得关于对象本身的很多信息。这样说来,Java也是内省的语言。
C在运行时中,只有一大堆机器码。在C++中,一些语言构件可以在编译后留下来。Java中,编译时和运行时界限十分模糊,可以有足够的内省能力来列出一个类的方法和继承关系等。
最彻底的是Ruby,Ruby中没有编译时的概念,所有的构件都在运行时可用。
元编程
比起内省,元编程更进一步,它允许对语言构件进行修改,举个例子:
在Java中, 假如想要写一个DBModel,必须要把数据库中的字段都一一对应写在类中,并添加getter和setter。而在数据库字段发生改变的时候,还必须相应地改变类的实例变量和对应的实例方法。而在Ruby中,只要写出如下的代码,就可以自动完成数据表的映射:
class Movie < ActiveRecord::Base
end
这样就能直接使用Movie#title()来访问title字段。这些方法和变量都没有定义过,那么ActiveRecorder是怎么实现的呢?
因为类名是Movie,ActiveRecord会把它映射到名为movies的表中。而那些访问属性的方法,是ActiveRecord从表模式中得到字段名后,自动定义的方法。
写入语言构件的能力就在这时候发挥作用了,至于这种能力是怎么实现的,在后面的书中会进行讲解。
元编程是编写在运行时操纵语言构件的代码。
这就是元编程的概念~
Open Class特性
Open Class特性 在Ruby中,第二次定义一个类只会修改已经存在的类,比如:
class D
def x
end
end
class D
def y
end
end
这样的话,D就有x和y两个方法了。
值得注意的是…这样很可能导致原来其他地方使用的方法被重写,从而导致运行出现问题。这是Open Class的隐患,经常被称作猴子补丁 Monkeypatch
为了避免猴子补丁的问题,我们可以将类放在模块中,这样就能很大程度上避免命名冲突了。(还是有可能会发生问题:只要两处的模块名相同且类名相同,就依然会有猴子补丁的问题)
另外还有其他的解决方案,选择子空间等等…当然,书中的mentor说在书后面会讲到,那就后面看到再说吧~
Ruby类的真相
在Ruby这样的动态语言中,对象的类和实例变量没有关系,当给实例变量赋值时,它们就生成了。如下例,输出是如果不曾调用my_method,那么输出的变量列表就是空
class MyClass
def my_method
@v=1
end
end
obj = MyClass.new
puts obj.instance_variables
实例变量存放在对象中,而方法(包括类方法和对象的方法)放在类中。
类自身也是对象。类自身的类叫做Class. 在这里类是一个对象,那么这个对象的类:Class也就拥有了这个对象的实例方法。所以一个类的方法就是Class的实例方法。
Class的superclass是Module Module的superclass时Object Object的superclass是BasicObject……MyClass BasicObject的superclass是nil
因此,Class只不过是在Module的基础上增加了new、allocate和superclass这三个方法的增强模块罢了。
可是运行Module.methods可以找到这三个方法?
首先,methods,private_methods是Object类的实例方法;instance_methods是Module类的实例方法。 Ruby对象可以分为2类,一类是普通对象,还有一类对象是类(类本身也是一种对象),像String,Class这种类,这种对象叫类对象。 普通对象的祖先链,以"abc"为例,为String-> Comparable->Object->Kernel-> BasicObject 类对象的祖先链,以String为例,为Class->Module->Object->Kernel-> BasicObject 我们可以看到普通对象是没有instance_methods方法的,因为其祖先链上没有Module类。所以对于一个普通对象,我们只能说它有方法或私用方法,而不能说它有实例方法,实例方法是对一个类来说的。 (引用自slef-motivation的博客https://blog.csdn.net/happyanger6/article/details/42436879)
所以运行Module.methods找到的是Object类中的方法,那时候自然会有这三个方法。在Module.instance_methods中就找不到这三个方法了。
Module类提供了两个constants()方法,实例方法Module#constants()返回当前范围内的常量,而类方法Module.constants()返回当前程序中所有的顶级常量,包括类名。
module MyClass AAA = 'aaa' def my_method @v=1 end end
MyClass.constsants #返回AAA
Module.constants #返回Object,Module,Class,BasicObject......MyClass。这些都是顶级常量(类名也是顶级常量),而AAA由于不是顶级的常量,所以没有被列在里面
当在一个类中包含一个模块时,Ruby创建了一个封装该模块的匿名类,并把这个匿名类插入到祖先链中。
module M
def my_method
end
end
class C
include M
end
puts C.ancestors
结果输出:
C
M
Object
Kernel
BasicObject
祖先链会包含模块,在这里是M和Kernel
那么Kernel模块又是什么呢?
Ruby中有像print()这类方法,我们可以在任何代码空间中调用它们,这实际上都是Kernel模块的私有方法。Object类包含了Kernel模块,而Object类是任何类的superclass,所以任何类都会包含Kernel模块,也就有了Kernel内的私有方法。
self
由于对象和类的方法分开,对象必须要有一个self引用才能让方法知道当前调用者是谁。
通常self是最后一个接收到方法调用的对象,但是当在类和模块中定义时,self的角色由类/模块担任。
class MyClass
self # => MyClass
end
所以在类中定义方法前加上self就是类方法/单例方法了,因为这时self是类自己
class MyClass
def self.claz_method
p 'this is a class method'
end
end
MyClass.claz_method
puts MyClass.singleton_methods(false) # => claz_method
Ruby中特殊的private规则
Ruby中不允许明确指定一个接收者来调用一个私有的方法。换言之,每次调用一个私有方法时,只能调用于隐含的接收者——self上。
class C
def public_method
self.private_method
end
private
def private_method
end
end
C.new.public_method
就算这样,指定self,依然会调用失败,因为要求只能调用于隐含的self。
关于self的一个案例
module Printable
def print
puts 'print in Printable'
end
end
module Document
def print_to_screen
print
end
def print
puts 'print in document'
end
end
class Book
include Document
include Printable
end
Book.new.print_to_screen
这里的输出结果是"print in Printable”
因为祖先链是:[Book, Printable, Document, Object , Kernel, BasicObject]
所以self在print_to_screen中寻找print方法时,由于self所属的Book类没有print方法,所以向从Book类向上寻找,而到Printable就已经找到方法了,就会调用Printable中的print方法。
总结
这就是今天的《Ruby元编程》抄写情况,谢谢观看~