1. 程式人生 > >Ruby學習之面向物件的使用

Ruby學習之面向物件的使用

Ruby是純面向物件的語言,其中的一切都是物件,對於這一點,相信大家都不會反駁,它其中最原始的值諸如字串、數字,甚至連 true 和 false 都是物件,類本身也是一個物件,是 Class 類的一個例項,它用於指定物件的形式,它結合了資料表示法和方法,把資料整理成一個整齊的包。類中的資料和方法被稱為類的成員。我們這次就來詳細的看下。

當我們定義一個類的時候,就相當於定義了一個數據型別的藍圖,實際上並沒有定義什麼實際的資料,只是定義了類的名稱意味著什麼,也就是說,定義了類的物件將由什麼組成,以及在該物件上能執行什麼操作。

類定義以關鍵字 class 開始,後跟類名稱,最後以一個 end

進行分隔表示終止該類定義,像下面這樣:

class Box
#類主體
end

按照慣例,名稱必須以大寫字母開頭,如果包含多個單詞,每個單詞首字母大寫,但此間沒有分隔符(例如:CamelCase)。

既然類提供了物件的藍圖,所以基本上,物件是根據類進行建立的。我們使用 new 關鍵字宣告類的物件,如下:

box1 = Box.new
box2 = Box.new

之後就要來說說initialize 方法了,它是一個標準的 Ruby 類方法,是類的建構函式,與其他面向物件程式語言中的 constructor 工作原理類似。所以說,當我們想要在建立物件的同時初始化一些類變數,initialize 方法就派上用場了。該方法帶有一系列引數,與其他 Ruby 方法一樣,使用該方法時,必須在前面放置 def

關鍵字,如下:

class Box
   def initialize(w,h)
      @width, @height = w, h
   end
end

再來就輪到例項變量了,它是類的屬性,它們在使用類建立物件時就變成物件的屬性。每個物件的屬性是單獨賦值的,和其他物件之間不共享值。在類的內部,是使用 @ 運算子訪問這些屬性,在類的外部,則是使用稱為訪問器方法公共方法進行訪問,來看下面的例項:

class Box
   def initialize(w,h)
      # 給例項變數賦值
      @width, @height = w, h
   end
end

為了在類的外部讀取類中已定義的變數,我們可以通過定義訪問器(getter)方法來訪問,來看下使用案例:

#!/usr/bin/ruby -w
 
# 定義類
class Box
   # 建構函式
   def initialize(w,h)
      @width, @height = w, h
   end
 
   # 訪問器方法
   def printWidth
      @width
   end
 
   def printHeight
      @height
   end
end
 
# 建立物件,初始化盒子的高度與寬度
box = Box.new(10, 20)
 
# 使用訪問器方法
x = box.printWidth()
y = box.printHeight()
 
puts "盒子寬度 : #{x}"
puts "盒子高度 : #{y}"

與用於訪問變數值的訪問器方法類似,Ruby 提供了一種在類的外部將引數傳入類中已定義的變數,也就是所謂的設定器方法,來看下使用案例:

#!/usr/bin/ruby -w
 
# 定義類
class Box
   # 構造器方法
   def initialize(w,h)
      @width, @height = w, h
   end
 
   # 訪問器方法
   def getWidth
      @width
   end
   def getHeight
      @height
   end
 
   # 設定器方法
   def setWidth=(value)
      @width = value
   end
   def setHeight=(value)
      @height = value
   end
end
 
# 建立物件
box = Box.new(10, 20)
 
# 使用設定器方法
box.setWidth = 30
box.setHeight = 50
 
# 使用訪問器方法
x = box.getWidth()
y = box.getHeight()
 
puts "盒子寬度 : #{x}"
puts "盒子高度 : #{y}"

由於兩種方法非常常用,Ruby 定義了 attr_accessor :variable_name、attr_reader :variable_name、attr_writer :variable_name 三種屬性宣告方法。其中:accessor=reader+writer。同時我們一定要注意,變數名前一定要帶 : ,變數名之間要用 , 分割。

再來就是例項方法了,它的定義與其他方法的定義一樣,都是使用 def 關鍵字,但它們只能通過類例項來使用,來看使用案例:

#!/usr/bin/ruby -w
 
# 定義類
class Box
   # 構造方法
   def initialize(w,h)
      @width, @height = w, h
   end
   # 例項方法
   def getArea
      @width * @height
   end
end
 
# 建立物件
box = Box.new(10, 20)
 
# 呼叫例項方法
a = box.getArea()
puts "Area of the box is : #{a}"

類變數是在類的所有例項中共享的變數。換句話說,類變數的例項可以被所有的物件例項訪問。類變數以兩個 @ 字元(@@)作為字首,類變數必須在類定義中被初始化。

類方法使用 def self.methodname() 定義,類方法以 end 分隔符結尾。類方法可使用帶有類名稱的 classname.methodname 形式呼叫,來看使用案例:

#!/usr/bin/ruby -w
 
class Box
   # 初始化類變數
   @@count = 0
   def initialize(w,h)
      # 給例項變數賦值
      @width, @height = w, h
 
      @@count += 1
   end
 
   def self.printCount()
      puts "Box count is : #@@count"
   end
end
 
# 建立兩個物件
box1 = Box.new(10, 20)
box2 = Box.new(30, 100)
 
# 呼叫類方法來輸出盒子計數
Box.printCount()

我們定義的任何類都有一個 to_s 例項方法來返回物件的字串表示形式,來看一個根據 width 和 height 表示 Box 物件的例項:

#!/usr/bin/ruby -w
 
class Box
   # 構造器方法
   def initialize(w,h)
      @width, @height = w, h
   end
   # 定義 to_s 方法
   def to_s
      "(w:#@width,h:#@height)"  # 物件的字串格式
   end
end
 
# 建立物件
box = Box.new(10, 20)
 
# 自動呼叫 to_s 方法
puts "String representation of box is : #{box}"

Ruby為我們提供了三個級別的例項方法保護,分別是 public、private 或 protected。但是 Ruby 不在例項和類變數上應用任何訪問控制,我們來分別看下這三個許可權:

  • Public 方法: Public 方法可被任意物件呼叫。預設情況下,方法都是 public 的,除了 initialize 方法總是 private 的。
  • Private 方法: Private 方法不能從類外部訪問或檢視。只有類方法可以訪問私有成員。
  • Protected 方法: Protected 方法只能被類及其子類的物件呼叫。訪問也只能在類及其子類內部進行。

來看個例項感受下:

#!/usr/bin/ruby -w
 
# 定義類
class Box
   # 構造器方法
   def initialize(w,h)
      @width, @height = w, h
   end
 
   # 例項方法預設是 public 的
   def getArea
      getWidth() * getHeight
   end
 
   # 定義 private 的訪問器方法
   def getWidth
      @width
   end
   def getHeight
      @height
   end
   # make them private
   private :getWidth, :getHeight
 
   # 用於輸出面積的例項方法
   def printArea
      @area = getWidth() * getHeight
      puts "Big box area is : #@area"
   end
   # 讓例項方法是 protected 的
   protected :printArea
end
 
# 建立物件
box = Box.new(10, 20)
 
# 呼叫例項方法
a = box.getArea()
puts "Area of the box is : #{a}"
 
# 嘗試呼叫 protected 的例項方法
box.printArea()

繼承,是面向物件程式設計中最重要的概念之一。繼承允許我們根據另一個類定義一個類,這樣使得建立和維護應用程式變得更加容易。繼承有助於重用程式碼和快速執行,不幸的是,Ruby 不支援多繼承,但是 Ruby 支援 mixins。mixin 就像是多繼承的一個特定實現,在多繼承中,只有介面部分是可繼承的。當建立類時,程式設計師可以直接指定新類繼承自某個已有類的成員,這樣就不用從頭編寫新的資料成員和成員函式。這個已有類被稱為基類或父類,新類被稱為派生類或子類。Ruby 也提供了子類化的概念,子類化即繼承。擴充套件一個類的語法非常簡單。只要新增一個 < 字元和父類的名稱到類語句中即可,來看例項:

#!/usr/bin/ruby -w
 
# 定義類
class Box
   # 構造器方法
   def initialize(w,h)
      @width, @height = w, h
   end
   # 例項方法
   def getArea
      @width * @height
   end
end
 
# 定義子類
class BigBox < Box
 
   # 新增一個新的例項方法
   def printArea
      @area = @width * @height
      puts "Big box area is : #@area"
   end
end
 
# 建立物件
box = BigBox.new(10, 20)
 
# 輸出面積
box.printArea()

我們可以在派生類中新增新的功能,但有時我們可能想要改變已經在父類中定義的方法的行為。這時我們可以保持方法名稱不變,過載方法的功能即可,案例如下:

#!/usr/bin/ruby -w
 
# 定義類
class Box
   # 構造器方法
   def initialize(w,h)
      @width, @height = w, h
   end
   # 例項方法
   def getArea
      @width * @height
   end
end
 
# 定義子類
class BigBox < Box
 
   # 改變已有的 getArea 方法
   def getArea
      @area = @width * @height
      puts "Big box area is : #@area"
   end
end
 
# 建立物件
box = BigBox.new(10, 20)
 
# 使用過載的方法輸出面積
box.getArea()

我們希望使用 + 運算子執行兩個 Box 物件的向量加法,使用 * 運算子來把 Box 的 width 和 height 相乘,使用一元運算子 - 對 Box 的 width 和 height 求反,來看例項:

class Box
  def initialize(w,h) # 初始化 width 和 height
    @width,@height = w, h
  end
 
  def +(other)         # 定義 + 來執行向量加法
    Box.new(@width + other.width, @height + other.height)
  end
 
  def [email protected]               # 定義一元運算子 - 來對 width 和 height 求反
    Box.new([email protected], [email protected])
  end
 
  def *(scalar)        # 執行標量乘法
    Box.new(@width*scalar, @height*scalar)
  end
end

有時候,我們想要防止物件被改變。在 Object 中,freeze 方法可實現這點,它能有效地把一個物件變成一個常量。任何物件都可以通過呼叫 Object.freeze 進行凍結。凍結物件不能被修改,也就是說,我們不能改變它的例項變數。我們可以使用 Object.frozen? 方法檢查一個給定的物件是否已經被凍結。如果物件已被凍結,該方法將返回 true,否則返回一個 false 值,來看個例項感受下:

#!/usr/bin/ruby -w
 
# 定義類
class Box
   # 構造器方法
   def initialize(w,h)
      @width, @height = w, h
   end
 
   # 訪問器方法
   def getWidth
      @width
   end
   def getHeight
      @height
   end
 
   # 設定器方法
   def setWidth=(value)
      @width = value
   end
   def setHeight=(value)
      @height = value
   end
end
 
# 建立物件
box = Box.new(10, 20)
 
# 讓我們凍結該物件
box.freeze
if( box.frozen? )
   puts "Box object is frozen object"
else
   puts "Box object is normal object"
end
 
# 現在嘗試使用設定器方法
box.setWidth = 30
box.setHeight = 50
 
# 使用訪問器方法
x = box.getWidth()
y = box.getHeight()
 
puts "Width of the box is : #{x}"
puts "Height of the box is : #{y}"

可以在類的內部定義一個常量,通過把一個直接的數值或字串值賦給一個變數來定義的,常量的定義不需要使用 @ 或 @@。按照慣例,常量的名稱使用大寫。一旦常量被定義,我們就不能改變它的值,我們可以在類的內部直接訪問常量,就像是訪問變數一樣,但是如果我們想要在類的外部訪問常量,那麼我們必須使用 classname::constant,來看例項感受下:

#!/usr/bin/ruby -w
 
# 定義類
class Box
   BOX_COMPANY = "TATA Inc"
   BOXWEIGHT = 10
   # 構造器方法
   def initialize(w,h)
      @width, @height = w, h
   end
   # 例項方法
   def getArea
      @width * @height
   end
end
 
# 建立物件
box = Box.new(10, 20)
 
# 呼叫例項方法
a = box.getArea()
puts "Area of the box is : #{a}"
puts Box::BOX_COMPANY
puts "Box weight is: #{Box::BOXWEIGHT}"

我們要知道,類常量可被繼承,也可像例項方法一樣被過載。

可能有一種情況,我們想要在不呼叫物件構造器 initialize 的情況下建立物件,即,使用 new 方法建立物件,在這種情況下,我們可以呼叫 allocate 來建立一個未初始化的物件,如下:

#!/usr/bin/ruby -w
 
# 定義類
class Box
   attr_accessor :width, :height
 
   # 構造器方法
   def initialize(w,h)
      @width, @height = w, h
   end
 
   # 例項方法
   def getArea
      @width * @height
   end
end
 
# 使用 new 建立物件
box1 = Box.new(10, 20)
 
# 使用 allocate 建立兩一個物件
box2 = Box.allocate
 
# 使用 box1 呼叫例項方法
a = box1.getArea()
puts "Area of the box is : #{a}"
 
# 使用 box2 呼叫例項方法
a = box2.getArea()
puts "Area of the box is : #{a}"

Ruby的 self 和 Java 的 this 有相似之處,但又大不相同。Java的方法都是在例項方法中引用,所以this一般都是指向當前物件的。而Ruby的程式碼逐行執行,所以在不同的上下文(context)self就有了不同的含義,來看例項:

#!/usr/bin/ruby -w
 
class Box
   # 輸出類資訊
   puts "Class of self = #{self.class}"
   puts "Name of self = #{self.name}"
end

上述程式碼意味著類定義可通過把該類作為當前物件來執行,同時也意味著元類和父類中的該方法在方法定義執行期間是可用的。

好啦,本次記錄就到這裡了。

如果感覺不錯的話,請多多點贊支援哦。。。