Effective Ruby

  1. 理解Ruby中的true
  2. 所有對象的值都可能為nil
  3. 避免使用Ruby中古怪的Perl風(fēng)格語法
  4. 常量是可變的
  5. 留意運行時警告
  6. 了解Ruby如何構(gòu)建繼承體系
  7. 了解super的不同行為
  8. 初始化子類時調(diào)用super
  9. 提防Ruby最棘手的解析
  10. 優(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異常
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 一、 讓自己熟悉Ruby 1、理解 Ruby 中的 True 在 Ruby 中,除了 false 和 nil, 其...
    Sgemini閱讀 711評論 0 1
  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 2,055評論 0 9
  • 重點掌握 3 類對象和方法 對象就是一個物體 類的獨特存在就是一個實例,對實例進(jìn)行操作叫做方法。方法可以應(yīng)用于類或...
    Coder大雄閱讀 1,368評論 0 2
  • 每當(dāng)我們失敗的時候,總會有人對我們說:“別怕,失敗乃成功之母,繼續(xù)走下走就對了?!边@個時候,我們會更堅定自己的內(nèi)心...
    張來福閱讀 634評論 0 0
  • 本節(jié)任務(wù) 初始化一個工程 第一步 創(chuàng)建一個文件目錄,這個目錄主要放我們的練習(xí)demo,我給它命名為weex** 第...
    酷走天涯閱讀 781評論 0 0

友情鏈接更多精彩內(nèi)容