程式扎記: [ Ruby Gossip ] Basic : 模組 - 定義模組

標籤

2014年11月16日 星期日

[ Ruby Gossip ] Basic : 模組 - 定義模組

Source From Here 
Preface 
Ruby 中,模組(Module)中可以定義變數與方法。透過適當的設計,你可以將物件之間可能共用的實作抽離至模組中,在必要時讓類別 include,使得類別定義時得以精簡。 

定義模組 
舉個例子來說,你會定義以下的球類別,並定義了一些比較大小的方法: 
  1. class Ball  
  2.     attr_reader :radius  
  3.   
  4.     def initialize(radius)  
  5.         @radius = radius  
  6.     end  
  7.   
  8.     def <=>(that) self.radius - that.radius end  
  9.   
  10.     def <(that)  (self <=> that) < 0 end  
  11.     def <=(that) (self < that) || (self <=> that) == 0 end  
  12.     def >(that)  !(self <= that) end  
  13.     def >=(that) !(this < that) end  
  14.     def ==(that) (self <=> that) == 0 end  
  15.       
  16.     def eql?(that)  
  17.         if self.equal?(that)  
  18.             return true  
  19.         end  
  20.         if that.is_a?(Ball)  
  21.             return self == that  
  22.         end  
  23.         return false  
  24.     end  
  25.       
  26.     def hash  
  27.         41 * @radius  
  28.     end  
  29.   
  30.     def to_s  
  31.         "Ball(#{@radius})"  
  32.     end  
  33. end  
事實上,比較大小順序這件事,許多物件都會用的到,仔細觀察以上的程式碼,你會發現可抽離的共用比較方法,你可以將之重新設計為模組: 
  1. module Comparable  
  2.     def <=>(that)  
  3.         raise RuntimeError, "必須實作 <=> 方法"  
  4.     end  
  5.       
  6.     def <(that)  (self <=> that) < 0 end  
  7.     def <=(that) (self < that) || (self <=> that) == 0 end  
  8.     def >(that)  !(self <= that) end  
  9.     def >=(that) !(this < that) end  
  10.     def ==(that) (self <=> that) == 0 end  
  11. end  
模組中除了<=>沒有實作之外,其它的方法都實作了。現在有了 Comparable 模組,你可以在設計球類別時更為精簡,如果你需要彼此比較的功能,則只要將 Comparable 模組 include 進來並重新定義 <=> 方法即可以: 
  1. class Ball  
  2.     include Comparable  
  3.   
  4.     attr_reader :radius  
  5.   
  6.     def initialize(radius)  
  7.         @radius = radius  
  8.     end  
  9.   
  10.     def <=>(that) self.radius - that.radius end  
  11.       
  12.     def eql?(that)  
  13.         if self.equal?(that)  
  14.             return true  
  15.         end  
  16.         if that.is_a?(Ball)  
  17.             return self == that  
  18.         end  
  19.         return false  
  20.     end  
  21.       
  22.     def hash  
  23.         41 * @radius  
  24.     end  
  25.   
  26.     def to_s  
  27.         "Ball(#{@radius})"  
  28.     end  
  29. end  
將 Comparable 模組 include 至 Ball 類別,如此模組中定義的方法,就會成為 Ball 的實例方法,在 Ruby 中稱這樣的機制為 Mix-in。事實上,Ruby 確實內建了 Comparable 模組來作比大小這種事,所以實際上你沒有撰寫上面的 Comparable 範例,你的 Ball 類別還是可以比大小,因為如此就是 include 內建的 Comparable 模組. 

類似地,如果你在收集物件之後,必須迭代、取得最大物件、最小物件、排序、尋找物件等動作,不用親自撰寫,只要 include 內建的 Enumerable 模組就可以了。例如: 
  1. class Pond  
  2.     include Enumerable  
  3.   
  4.     def initialize(list = [])  
  5.         @list = list  
  6.     end  
  7.     def <<(obj)  
  8.         @list << obj  
  9.     end  
  10.   
  11.     def each  
  12.         @list.each { |obj|  
  13.             yield(obj)  
  14.         }  
  15.     end  
  16. end  
  17.   
  18. pond = Pond.new([Ball.new(10), Ball.new(20)])  
  19. pond << Ball.new(5)  
  20. pond << Ball.new(15)  
  21. pond << Ball.new(10)  
  22.   
  23. puts pond.include? Ball.new(5)  # true  
  24. print "#{pond.sort}\n"   # [Ball(5), Ball(10), Ball(10), Ball(15), Ball(20)]  
  25. puts "Max: #{pond.max}"  # Max: Ball(20)  
  26. puts "Min: #{pond.min}"  # Min: Ball(20)  
  27.   
  28. pond.each_with_index do |ball, index|  
  29.     puts "#{index} - #{ball}"  
  30. end  
將 Enumerable 模組include至類別之後,唯一要實作的就是 each 方法,實作如何逐一迭代,其餘的方法,Enumerable 皆已經使用 each 方法為基礎實作完成,所以你可以直接呼叫使用。 

Supplement 
Ruby 手冊 - 模組 modules 
Ruby tutorialspoint - Modules & Mixin

沒有留言:

張貼留言

網誌存檔

關於我自己

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