本次學習資料
通讀《Ruby元程式設計》,記錄下有趣的以及不明白的知識點
元程式設計
元程式設計是能編寫能寫代碼的代碼。
編寫在運作時操作自身的代碼,稱為動态元程式設計,代碼生成器和編譯器的那種方式稱為靜态元程式設計。Ruby不需要編譯,是以非常适合元程式設計。
内省機制
inspection,意思為檢查,檢討的意思,是以顧名思義,就是可以檢測自己屬于什麼類,自己有沒有某個方法,自己的父類是誰。
◆:一個ruby對象o 的内省
1:得到自己的object id: o.object_id
2:檢測自己的類型 inspect type: [].class == Array
3:檢測自己的類:inspect class: o.class == Foo o.instance_of?(Foo)
4:檢測類的繼承:inspect class hierarchy: o.class.superclass o.class.included_modules
5:檢測自己是否擁有某個方法: o.respond_to?(“reverse”)
6:檢測自己有哪些執行個體方法(false表示隻要類自己的方法,不要繼承的):o.class.instance_methods(false)
7:檢測自己有哪些執行個體變量:o.class.instance_variables
tips:Active Record通過内省機制檢視類的名字,根據類名自動的映射到對應的表(類名:駝峰命名法,表名:小寫,以“_”分割),然後讀取表中字段,自動定義同名的屬性和相應的通路器。
打開類、猴子更新檔、細化(了解)
Ruby的class關鍵字更像是一個作用域操作符,而不是類型聲明語句。class關鍵字的核心任務是把你帶到類的上下文中,讓你可以在裡面定義方法。
在Ruby中,定義一個未存在的類可以定義這個類,若定義一個存在的類,就不再定義,會打開這個類,進行這次定義中的操作。比如,可以定義String類來增加新的方法。
class String
def to_asd
"asd"
end
end
a = "qwe"
puts a.to_asd
#=>asd
但若是String本身就有to_asd方法,則會覆寫掉原本的to_asd方法,導緻Bug。是以這種魯莽的修改類的方式叫做猴子更新檔。
為了解決猴子更新檔問題,需要用到細化refine,細化和打開類的差別在于細化不是全局性的,細化就像在原來的類上加了一塊更新檔,而且它會覆寫正常的方法查找,此外,細化隻在程式的部分區域生效,從using語句的位置開始到子產品或檔案結束。
module StringExtensions
refine String do
def reverse
"esrever"
end
end
end
module StringStuff
using StringExtensions
puts "my_string".reverse
end
puts "my_string".reverse
#=>esrever
#=>gnirts_ym
類似的,細化也有陷阱,比如:
class MyClass
def my_method
puts "original"
end
def another_method
my_method
end
end
module MyClassRefinement
refine MyClass do
def my_method
puts "refined"
end
end
end
using MyClassRefinement
MyClass.new.my_method
MyClass.new.another_method
#=>refined
#=>original
可以看出盡管調用了using,another_method仍然調用的using前的my_method。
類與子產品
子產品基本上就是一堆方法組成的包。
每個類都是一個子產品,類就是帶有三個方法(new,allocate,superclass)的增強子產品,通過這三個方法可以組織類的繼承結構,并建立對象。
Ruby中的類和子產品的概念十分接近,完全可以将二者互相替代,之是以同時保留二者的原因是為了保持代碼的清晰性,讓代碼意圖更加明确。使用原則:
1.希望把自己代碼包含(include)到别的代碼中,應該使用子產品
2.希望某段代碼被執行個體化或被繼承,應該使用類
子產品機制可以用來實作類似其它語言中的命名空間(Namespace)概念
類與對象
類就是一個對象(Class類的一個執行個體)外加一組執行個體方法和一個對其超類的引用。Class類是Module類的子類,是以一個類也是一個子產品。
對象就是一組執行個體變量外加一個指向其類的引用。對象的方法并不存在于對象本身,而是存在于對象的類中。
對象與類與子產品關系圖
常量與命名空間與 ::
Ruby中任何以大寫字母開頭的引用都是常量,常量的路徑(作用域),類似與檔案系統中的目錄,通過::進行分割和通路,預設直接以::開頭(例: :: Y)表示變量路徑的根位置
Y = "outside"
module Constant
Y = "inside"
class Inside
Y = "in inside"
end
end
puts Constant::Inside::Y
puts Constant::Y
puts ::Y
#=> in inside
#=> inside
#=> outside
Include、Extend、 Load、Require 的使用差別
1.include和extend用于在類中引入子產品,差別:
include:把子產品中的方法作為類的執行個體方法
extend: 把子產品中的方法作為類的類方法
module A
def class_type
puts "This class is of type:#{self.class}"
end
end
class B extend A end
class C include A end
B.class_type
C.new.class_type
2.require、load用于加載庫,如.rb檔案,差別:
require: 加載一個庫,并且隻加載一次,如果多次加載會傳回false。檔案不需要擴充名。一般用于導入類庫。
load: 加載一個庫,可多次加載。需要指定檔案擴充名。一般用于加載代碼。load可以通過load(‘xxx.rb’,true)來讓xxx.rb中的常量僅在自身範圍内有效。
#在目前目錄中有一個 two.rb檔案
require './two';
load './two.rb';
接收者與祖先鍊與prepend、include
接收者就是調用方法所在的對象,比如,my_string.reverse()中,my_string就是接收者。
一個類找到它的超類,然後再找到超類的超類,以此類推直到找到BasicObject類,這個過程中經曆的類路徑就是該類的祖先鍊。
因為類與子產品是父子關系,是以祖先鍊中也可以包含子產品,prepend與include分别可以向鍊中添加子產品,不同的是調用include方法,子產品會被插入祖先鍊,目前類的正上方,而prepend同樣是插入到祖先鍊,但位置其他卻在目前類的正下方,另外通過Class.ancestors可以檢視目前的祖先鍊。
module M1
def my_methods
'M1#my_methods()'
end
end
class C
include M1
end
class D < C; end
puts D.ancestors
#=> D
#=> C
#=> M1
#=> Object
#=> Kernel
#=> BasicObject
tips: 如果多次引用同一個子產品,ruby會忽略掉這個引用指令。
self
1.調用一個方法時,接收者會扮演self角色
2.定義一個子產品(或類)時,該子產品(或類)會扮演self角色
3.執行個體變量永遠被認定為self的執行個體變量
4.任何沒有明确指定接收者的方法調用,都當做是調用self的方法
class MyClass
def testing_self
@var = 10
my_method()
puts self
puts "#{@var}"
end
def my_method
@var = @var + 1
end
end
obj = MyClass.new
obj.testing_self
#=>#<MyClass:0x00007f9353874680>
#=>11
private
不能通過明确指定接受者來調用私有方法。私有方法隻能通過隐性的接受者self調用.