程式扎記: [ Ruby Gossip ] Basic : 類別 - 定義類別

標籤

2014年10月27日 星期一

[ Ruby Gossip ] Basic : 類別 - 定義類別

Source From Here 
Preface 
在 Ruby 中要定義類別非常的簡單,例如你可以定義一個帳戶(Account)類別: 
  1. # encoding: utf-8  
  2. class Account; end  
  3.   
  4. acct = Account.new  
  5.   
  6. def acct.initialize(number, name, balance)  
  7.     @number = number  
  8.     @name = name  
  9.     @balance = balance  
  10. end  
  11.   
  12. def acct.number  
  13.     @number  
  14. end  
  15.   
  16. def acct.name  
  17.     @name  
  18. end  
  19.   
  20. def acct.balance  
  21.     @balance  
  22. end  
  23.   
  24. def acct.deposit(money)  
  25.     if money <= 0  
  26.         raise  ArgumentError, "必須是正數"  
  27.     end  
  28.     @balance += money  
  29. end  
  30.   
  31. def acct.withdraw(money)  
  32.     if money > @balance  
  33.         raise  RuntimeError, "餘額不足"  
  34.     end  
  35.     @balance -= money  
  36. end  
  37.   
  38. acct.initialize("123-456-789""Justin"0)  
  39. puts acct.number    # 123-456-789  
  40. puts acct.name      # Justin  
  41.   
  42. acct.deposit(100)  
  43. puts acct.balance   # 100  
  44. acct.withdraw(50)  
  45. puts acct.balance   # 50  
上例中,class 用來定義一個類別,為了突顯 Ruby 可直接於類別實例上定義方法的特性,現在還沒有定義類別內容。要建立類別的實例(Instance),直接呼叫 Account.new 來建立。建立物件之後,可以直接於物件建立單例方法(Singleton method),透過 . 來傳送訊息給接收者,執行對應的方法。 

定義類別 
以 @ 開頭的變數稱為 實例變數Instance variable),顧名思義,為個別物件擁有的變數,實例變數無法直接由外部存取,必須自行定義方法,外部才可以取得實例變數的值,這也就是為何,上例中要再特別定義 name、number、balance 等方法的原因。上面這個例子定義了類別,但沒有封裝的概念,deposit、withdraw 等方法,都僅屬於 acct 參考的物件擁有,可以將物件建立後的初始化動作,以及會用到的相關操作定義在類別之中。來看看下面這個例子: 
  1. # encoding: utf-8  
  2. class Account  
  3.     def initialize(number, name, balance)  
  4.         @number = number  
  5.         @name = name  
  6.         @balance = balance  
  7.     end  
  8.       
  9.     def number  
  10.         @number  
  11.     end  
  12.   
  13.     def name  
  14.         @name  
  15.     end  
  16.       
  17.     def name=(value)  
  18.         @name = value  
  19.     end  
  20.   
  21.     def balance  
  22.         @balance  
  23.     end  
  24.   
  25.     def deposit(money)  
  26.         if money <= 0  
  27.             raise  ArgumentError, "必須是正數"  
  28.         end  
  29.         @balance += money  
  30.     end  
  31.   
  32.     def withdraw(money)  
  33.         if money > @balance  
  34.             raise  RuntimeError, "餘額不足"  
  35.         end  
  36.         @balance -= money  
  37.     end  
  38. end  
  39.   
  40. acct = Account.new("123-456-789""Justin"0)  
  41.   
  42. puts acct.number    # 123-456-789  
  43. puts acct.name      # Justin  
  44.   
  45. acct.deposit(100)  
  46. puts acct.balance   # 100  
  47. acct.withdraw(50)  
  48. puts acct.balance   # 50  
  49.   
  50. acct.name = "Caterpillar"  
  51. puts acct.name      # caterpillar  
呼叫 Account.new 建立實例之後,會自動呼叫 initialize 方法進行初始化的動作,傳給 Account.new 的引數,會依序傳給 initialize 方法作為引數(如果要定義物件被銷毀前要執行的方法,可以搜尋 ObjectSpace.define_finalizer 方法的使用)。如果要使用等號指定,可以自行定義如 name= 的方法,將實例方法(Instance method)與實例變數定義在類別之中,每個建構的實例就都會擁有類別中定義的方法與各自的實例變數。 

上例中,建立物件並初始化之後,為了可以取得與設定實例變數,必須自行定義方法。實際上,可以使用 attr_reader 方法定義取值方法,attr_writer 定義設值方法,或者使用 attr_accessor 同時定義取值與設值方法,attr_readerattr_writer 與 attr_accessor 接受的是 符號型態 實例。例如: 
  1. # encoding: Big5  
  2. class Account  
  3.     attr_reader   :number, :balance  
  4.     attr_accessor :name  
  5.       
  6.     def initialize(number, name, balance)  
  7.         @number = number  
  8.         @name = name  
  9.         @balance = balance  
  10.     end  
  11.   
  12.     def deposit(money)  
  13.         if money <= 0  
  14.             raise  ArgumentError, "必須是正數"  
  15.         end  
  16.         @balance += money  
  17.     end  
  18.   
  19.     def withdraw(money)  
  20.         if money > @balance  
  21.             raise  RuntimeError, "餘額不足"  
  22.         end  
  23.         @balance -= money  
  24.     end  
  25. end  
  26.   
  27. acct = Account.new("123-456-789""Justin"0)  
  28.   
  29. puts acct.number    # 123-456-789  
  30. puts acct.name      # Justin  
  31.   
  32. acct.deposit(100)  
  33. puts acct.balance   # 100  
  34. acct.withdraw(50)  
  35. puts acct.balance   # 50  
  36.   
  37. acct.name = "Caterpillar"  
  38. puts acct.name  
在 Ruby 中定義類別之後,可以在後續再度開啟類別增加新的定義,所有已建構的實例,將直接擁有增加的新定義。例如 陣列型態 中就看過這麼一個例子: 
 

Array 原本沒有 ^ 方法,在上例中開啟 Array 類別定義了 ^ 方法,使 Array 實例都可以呼叫 ^ 方法。可以開啟類別定義新方法,既有的類別已定義的方法,也可以移除,只要使用remove_method 方法(若必要,也可以移除實例變數,可搜尋 remove_instance_variable 方法的使用)。例如: 
 

在 Ruby,定義於類別中的方法,預設會是公開(public),可以使用 public、private或protected 方法定義指定的方法為公開、私有或受保護,公開的方法可以直接指定物件接收者(包括self)直接呼叫,私有方法則只能在有繼承關係的類別體系中不透過 self 呼叫,受保護方法則可以在有繼承關係的類別體系中指定物件接收者(包括self)呼叫。例如:: 
 
initialize方法一定是private。 

可以使用 instance_of? 方法測試物件是否為某個類別的實例,如果要知道是否為某個繼承體系的實例,則可以使用 is_a? 或 === 方法。例如: 
>> s.instance_of? Some
=> true
>> s.instance_of? Object
=> false
>> s.is_a? Object
=> true
>> s.is_a? Some
=> true
>> Some === s
=> true
>> Object === s
=> true

之後會學到繼承,在 Ruby 中,所有的類別都是 Object 的子類別,因此所有物件都是一種 Object。 

Supplement 
Ruby 手冊 - 類別 
Ruby tutorialspoint - Ruby Classes and Objects

沒有留言:

張貼留言

網誌存檔

關於我自己

我的相片
Where there is a will, there is a way!