- 理解Ruby中的true
- 所有對象的值都可能為nil
- 避免使用Ruby中古怪的Perl風(fēng)格語法
- 常量是可變的
- 留意運行時警告
- 了解Ruby如何構(gòu)建繼承體系
- 了解super的不同行為
- 初始化子類時調(diào)用super
- 提防Ruby最棘手的解析
- 優(yōu)先使用實例變量而非類變量
1. 理解Ruby中的true
- 除了false和nil之外的所有值都表示為真值
- 使用nil?或者“==”方法來區(qū)分nil和false
代碼如下:
#區(qū)分nil
nil.nil? #=>true
false.nil? #=>false
#區(qū)分false,需要將false放在“==”表達(dá)式的左邊,因為“==”是BasicObject的方法,能被普通類繼承和改寫
class Bad
def ==(other)
true
end
end
false == Bad.new #=>false
Bad.new == false #=>true
#上面的Bad.new和false相當(dāng)于調(diào)用了“==”方法
- true和false其實是不遵循命名和賦值規(guī)范的全局變量
false.class #=>TrueClass
true.class #=>FalseClass
true和false的行為與任何對象一樣,能夠調(diào)用它們之上的方法。
**2. 所有對象的值都可能為nil **
做開發(fā)的過程中進(jìn)場會遇到下面的錯誤提示:
undefined method "xx" for nil:NilClass(NoMethodError)
其實這個問題是可以通過相應(yīng)的方法進(jìn)行規(guī)避的,下面用代碼介紹幾種方式:
#使用nil?方法
person.save if person
person.save if !person.nil?
person.save unless person.nil?
#將對象轉(zhuǎn)換為期望的類型
nil.to_s #=> " "
nil.to_a #=> []
nil.to_i #=> 0
nil.to_f #=> 0.0
#Array#compact方法返回去掉所有nil元素的方法的接受者的副本
name = [first, middle, last].compact.join(" ")
3. 避免使用Ruby中古怪的Perl風(fēng)格語法
- 使用String#match替代String#=~,前者將匹配信息以MatchData對象返回
#對比下面的兩段代碼
def extract_error(message)
if message =~ /^ERROR:\s+(.+)$/
$1
else
"no error"
end
end
def extract_error(message)
if m = message.match(/^ERROR:\s+(.+)$/)
m[1]
else
"no error"
end
end
- 使用更長,更表意的全局變量的別名,比如使用$LOAD_PATH代替$:
- 避免使用隱式讀寫全局變量$_的方法
while readline
print if ~ /^ERROR:/
end
4. 常量是可變的
常量:由大寫字母開頭的任何標(biāo)識符都是常量,比如類名和模塊名,比如。
#單個字符串的常量是可以被重新命名的,會出現(xiàn)警告,但是命名還是會成功,解決方式是將其放在模塊中
module Defaults
TIMEOUT = 5
end
Defaults.freeze
Defaults::TIMEOUT = 10 #不能命令
#對數(shù)組對象而言,只是凍結(jié)了數(shù)組對象,而沒有凍結(jié)數(shù)組中的元素,這是淺凍結(jié)
#和clone和dup方法類似,這兩個方法也是淺拷貝
array = ["a", "b"]
array.freeze
array << "c" #失敗,因為該數(shù)組進(jìn)行了freeze
array.each{|obj| obj << "c"} #成功,值為["ac", "bc"],說明值并沒有進(jìn)行freeze
#解決方式是對數(shù)組進(jìn)行freeze的同時對數(shù)組內(nèi)的每一個元素也進(jìn)行freeze
array.each(&:freeze).freeze #對數(shù)組進(jìn)行freeze的同時對數(shù)組內(nèi)的元素freeze
array.each{|obj| obj << "c"} #失敗
5. 留意運行時警告
開啟運行時警告
ruby -w script.rb
6. 了解Ruby如何構(gòu)建繼承體系
- 對象方法的查找順序:對象的單例類,類,模塊(如果在類中include),Object,Kernel,BasicObject,如果沒有找到這個方法,會搜索method_missing方法
- 包含模塊時會創(chuàng)建單例類,并將其插入在繼承體系中包含它的類的上方。
- singleton_class #返回接受者的單例類
ancestor #繼承體系中所有類和模塊的數(shù)組,不包括單例類
included_modules #返回和ancestor一樣的數(shù)組,不過類被過濾掉。
7. 了解super的不同行為
- super等價于將宿主方法的所有參數(shù)傳遞給要調(diào)用的方法
- super(),不向重載方法傳遞任何參數(shù)
- super(x,y),調(diào)用相應(yīng)的參數(shù)內(nèi)容
- super是從整個繼承體系中尋找方法,所以也包括包括模塊的部分
例子總結(jié)中找。
8. 初始化子類時調(diào)用super
- initialize是私有實例方法,子類如果沒有重寫這個方法,會默認(rèn)繼承父類的initialize,類調(diào)用new方法時,會自動調(diào)用initialize方法
class Father
def initialize
@name = "jayzen"
end
def to_s
"#{self.class}"+" #{@name}"
end
end
class Child < Father
end
obj = Child.new
#默認(rèn)繼承了父類的initialize
puts obj #=>Child jayzen
- 子類自定義initialize方法時,會對父類的initialize進(jìn)行重載(方法名字相同,但是方法參數(shù)不同),但是子類不會調(diào)用父類的被重載的方法initialize,就是說子類一旦定義了initialize方法,父類的initialize方法就不會被執(zhí)行
class Parent
attr_accessor :name
def initialize
@name = "world"
end
end
class Child < Parent
attr_accessor :grade
def initialize
@grade = "12"
end
end
youngster = Child.new
#因為繼承了父類的attr_accessor :name
puts youngster.respond_to?(:name) #true
#沒有調(diào)用父類的initialize方法,因此該值為nil
puts youngster.name #nil
- 使用super方法初始化父類
class Parent
attr_accessor :name
def initialize
@name = "jayzen"
end
end
class Child < Parent
attr_accessor :grade
def initialize
super
@grade = “marshal”
end
end
youngster = Child.new
puts youngster.name #=>jayzen
puts youngster.grade #=>Marshal
- 使用super方法使用參數(shù)初始化父類
class Parent
attr_accessor :name
def initialize(name)
@name = name
end
end
class Child < Parent
attr_accessor :grade
def initialize(name, grade)
super(name)
@grade = grade
end
end
youngster = Child.new("jayzen", "Marshal")
puts youngster.name #=>jayzen
puts youngster.grade #=>Marshal
9. 提防Ruby最棘手的解析
- 在setter方法中需要顯式的接受者,如果沒有接受者,會被ruby解析為變量賦值。
- 在實例方法中調(diào)用setter方法時,使用self作為接受者。
#定義一個setter方法
class SetMe
def initialize
@value = 0
end
def value
@value
end
def value=(x)
@value=x
end
end
x = SetMe.new
#允許在等號兩邊加空格,其實是setter方法,需要進(jìn)行顯示調(diào)用
x.value = 1
x.value #=>1
#在實例方法中調(diào)用setter方法時,使用self作為接受者
class Counter
attr_accessor(:counter)
def initialize
#沒有加self,表示的是局部變量,跳出def之后就無效了
counter = 0
#加上self,表示是setter方法
self.counter = 0
end
end
10. 推薦使用Struct而非Hash存儲結(jié)構(gòu)化數(shù)據(jù)
11. 通過在模塊中嵌入代碼來創(chuàng)建命名空間
12. 理解等價的不同用法
13. 通過“<=>”操作符實現(xiàn)比較和比較模塊
14. 通過protected方法共享私有狀態(tài)
15. 優(yōu)先使用實例變量而非類變量
#子類繼承超類的實例方法,也繼承超類的類方法
#超類中的類變量會被所有子類共享
class Father
private_class_method(:new)
def self.instance
@@single ||= new
end
def self.instance=(name)
@@single = name
end
end
class Configuration < Father
end
class Database < Father
end
puts Configuration.instance #class Father
private_class_method(:new)
def self.instance
@@single ||= new
end
def self.instance=(name)
@@single = name
end
end
class Configuration < Father
end
class Database < Father
end
puts Configuration.instance #=>#<Configuration:xxx>
Configuration.instance = "string"
puts Database.instance #=>"string"
#類也是對象,所有他們擁有自己的私有實例變量集合
def self.instance
@single ||= new
end
Configuration.instance #=>#<Configuration:xxx>
Database.instance #=>#<Database:xxx>
22. 使用定制的異常而不是拋出字符串
- raise 默認(rèn)拋出的RuntimeError異常
- rescue 默認(rèn)截獲的StandardError異常