Ruby - 面向物件



Ruby 是一種純面向物件的語言,在 Ruby 中,一切皆物件。Ruby 中的每個值都是物件,即使是最原始的東西:字串、數字,甚至是真假值。甚至類本身也是一個物件,它是 Class 類的例項。本章將帶您瞭解與面向物件 Ruby 相關的所有主要功能。

類用於指定物件的形態,它將資料表示和操作該資料的方法組合成一個簡潔的包。類中的資料和方法稱為類的成員。

Ruby 類定義

定義類時,您定義了資料型別的藍圖。這實際上並沒有定義任何資料,但它確實定義了類名的含義,即類的物件將包含什麼以及可以對這樣的物件執行哪些操作。

類定義以關鍵字 **class** 後跟 **類名** 開始,並以 **end** 結束。例如,我們使用關鍵字 class 定義了 Box 類,如下所示:

class Box
   code
end

名稱必須以大寫字母開頭,按照慣例,包含多個單詞的名稱將連在一起,每個單詞的首字母大寫,並且沒有分隔符(駝峰命名法)。

定義 Ruby 物件

類為物件提供藍圖,因此基本上物件是從類建立的。我們使用 **new** 關鍵字宣告類的物件。以下語句聲明瞭兩個 Box 類的物件:

box1 = Box.new
box2 = Box.new

initialize 方法

**initialize 方法** 是標準的 Ruby 類方法,其工作方式與其他面向物件程式語言中的 **建構函式** 幾乎相同。當您想要在建立物件時初始化一些類變數時,initialize 方法很有用。此方法可以接受引數列表,並且像任何其他 ruby 方法一樣,它前面都會加上 **def** 關鍵字,如下所示:

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

例項變數

**例項變數** 是一種類屬性,一旦使用類建立物件,它們就成為物件的屬性。每個物件的屬性都是單獨分配的,與其他物件不共享任何值。它們在類中使用 @ 運算子訪問,但要在類外部訪問它們,我們使用 **public** 方法,這些方法稱為 **訪問器方法**。如果我們採用上面定義的類 **Box**,則 @width 和 @height 是 Box 類的例項變數。

class Box
   def initialize(w,h)
      # assign instance variables
      @width, @height = w, h
   end
end

訪問器和設定器方法

為了使變數能夠從類外部訪問,必須在 **訪問器方法** 中定義它們,這些訪問器方法也稱為 getter 方法。下面的例子展示了訪問器方法的使用:

#!/usr/bin/ruby -w

# define a class
class Box
   # constructor method
   def initialize(w,h)
      @width, @height = w, h
   end

   # accessor methods
   def printWidth
      @width
   end

   def printHeight
      @height
   end
end

# create an object
box = Box.new(10, 20)

# use accessor methods
x = box.printWidth()
y = box.printHeight()

puts "Width of the box is : #{x}"
puts "Height of the box is : #{y}"

執行上述程式碼時,會產生以下結果:

Width of the box is : 10
Height of the box is : 20

與用於訪問變數值的訪問器方法類似,Ruby 提供了一種方法,可以使用 **設定器方法** 從類外部設定這些變數的值,定義如下:

#!/usr/bin/ruby -w

# define a class
class Box
   # constructor method
   def initialize(w,h)
      @width, @height = w, h
   end

   # accessor methods
   def getWidth
      @width
   end
   def getHeight
      @height
   end

   # setter methods
   def setWidth=(value)
      @width = value
   end
   def setHeight=(value)
      @height = value
   end
end

# create an object
box = Box.new(10, 20)

# use setter methods
box.setWidth = 30
box.setHeight = 50

# use accessor methods
x = box.getWidth()
y = box.getHeight()

puts "Width of the box is : #{x}"
puts "Height of the box is : #{y}"

執行上述程式碼時,會產生以下結果:

Width of the box is : 30
Height of the box is : 50

例項方法

**例項方法** 也與我們使用 **def** 關鍵字定義任何其他方法的方式相同,並且只能使用類例項來使用它們,如下所示。它們的功能不限於訪問例項變數,還可以根據您的需求執行更多操作。

#!/usr/bin/ruby -w

# define a class
class Box
   # constructor method
   def initialize(w,h)
      @width, @height = w, h
   end
   # instance method
   def getArea
      @width * @height
   end
end

# create an object
box = Box.new(10, 20)

# call instance methods
a = box.getArea()
puts "Area of the box is : #{a}"

執行上述程式碼時,會產生以下結果:

Area of the box is : 200

類方法和變數

**類變數** 是一個變數,它在類的所有例項之間共享。換句話說,該變數只有一個例項,並且可以透過物件例項訪問它。類變數以兩個 @ 字元 (@@) 為字首。類變數必須在類定義中初始化,如下所示。

類方法使用 **def self.methodname()** 定義,它以 end 分隔符結尾,並將使用類名作為 **classname.methodname** 來呼叫,如以下示例所示:

#!/usr/bin/ruby -w

class Box
   # Initialize our class variables
   @@count = 0
   def initialize(w,h)
      # assign instance avriables
      @width, @height = w, h

      @@count += 1
   end

   def self.printCount()
      puts "Box count is : #@@count"
   end
end

# create two object
box1 = Box.new(10, 20)
box2 = Box.new(30, 100)

# call class method to print box count
Box.printCount()

執行上述程式碼時,會產生以下結果:

Box count is : 2

to_s 方法

您定義的任何類都應該具有 **to_s** 例項方法以返回物件的字串表示形式。以下是一個簡單的示例,用於根據寬度和高度表示 Box 物件:

#!/usr/bin/ruby -w

class Box
   # constructor method
   def initialize(w,h)
      @width, @height = w, h
   end
   # define to_s method
   def to_s
      "(w:#@width,h:#@height)"  # string formatting of the object.
   end
end

# create an object
box = Box.new(10, 20)

# to_s method will be called in reference of string automatically.
puts "String representation of box is : #{box}"

執行上述程式碼時,會產生以下結果:

String representation of box is : (w:10,h:20)

訪問控制

Ruby 在例項方法級別提供了三個級別的保護,可以是 **public、private 或 protected**。Ruby 對例項變數和類變數不應用任何訪問控制。

  • **公共方法** - 公共方法可以被任何人呼叫。方法預設情況下是公共的,除了 initialize,它始終是私有的。

  • **私有方法** - 私有方法無法從類外部訪問,甚至無法檢視。只有類方法可以訪問私有成員。

  • **受保護的方法** - 受保護的方法只能由定義類及其子類的物件呼叫。訪問許可權保持在家族內部。

以下是一個簡單的示例,用於演示所有三種訪問修飾符的語法:

#!/usr/bin/ruby -w

# define a class
class Box
   # constructor method
   def initialize(w,h)
      @width, @height = w, h
   end

   # instance method by default it is public
   def getArea
      getWidth() * getHeight
   end

   # define private accessor methods
   def getWidth
      @width
   end
   def getHeight
      @height
   end
   # make them private
   private :getWidth, :getHeight

   # instance method to print area
   def printArea
      @area = getWidth() * getHeight
      puts "Big box area is : #@area"
   end
   # make it protected
   protected :printArea
end

# create an object
box = Box.new(10, 20)

# call instance methods
a = box.getArea()
puts "Area of the box is : #{a}"

# try to call protected or methods
box.printArea()

執行上述程式碼時,會產生以下結果。這裡,第一個方法成功呼叫,但第二個方法出現了問題。

Area of the box is : 200
test.rb:42: protected method `printArea' called for #
<Box:0xb7f11280 @height = 20, @width = 10> (NoMethodError)

類繼承

面向物件程式設計中最重要的概念之一是繼承。繼承允許我們根據另一個類來定義類,這使得建立和維護應用程式更加容易。

繼承還提供了重用程式碼功能和加快實現時間的機會,但不幸的是,Ruby 不支援多級繼承,但 Ruby 支援 **mixin**。mixin 類似於多重繼承的專門實現,其中只繼承介面部分。

建立類時,程式設計師可以指定新類應該繼承現有類的成員,而不是編寫全新的資料成員和成員函式。這個現有類稱為 **基類或超類**,新類稱為 **派生類或子類**。

Ruby 還支援子類化,即繼承,以下示例解釋了這個概念。擴充套件類的語法很簡單。只需將 < 字元和超類的名稱新增到您的類語句中即可。例如,以下將定義一個類 *BigBox* 作為 *Box* 的子類:

#!/usr/bin/ruby -w

# define a class
class Box
   # constructor method
   def initialize(w,h)
      @width, @height = w, h
   end
   # instance method
   def getArea
      @width * @height
   end
end

# define a subclass
class BigBox < Box

   # add a new instance method
   def printArea
      @area = @width * @height
      puts "Big box area is : #@area"
   end
end

# create an object
box = BigBox.new(10, 20)

# print the area
box.printArea()

執行上述程式碼時,會產生以下結果:

Big box area is : 200

方法重寫

雖然您可以在派生類中新增新功能,但有時您可能想要更改父類中已定義方法的行為。您可以簡單地保持方法名稱相同並重寫方法的功能,如下面的示例所示:

#!/usr/bin/ruby -w

# define a class
class Box
   # constructor method
   def initialize(w,h)
      @width, @height = w, h
   end
   # instance method
   def getArea
      @width * @height
   end
end

# define a subclass
class BigBox < Box

   # change existing getArea method as follows
   def getArea
      @area = @width * @height
      puts "Big box area is : #@area"
   end
end

# create an object
box = BigBox.new(10, 20)

# print the area using overriden method.
box.getArea()

運算子過載

我們希望 + 運算子使用 + 執行兩個 Box 物件的向量加法,* 運算子將 Box 的寬度和高度乘以標量,而一元 - 運算子則對 Box 的寬度和高度取反。這是一個具有定義的數學運算子的 Box 類的版本:

class Box
   def initialize(w,h)     # Initialize the width and height
      @width,@height = w, h
   end

   def +(other)       # Define + to do vector addition
      Box.new(@width + other.width, @height + other.height)
   end

   def -@           # Define unary minus to negate width and height
      Box.new(-@width, -@height)
   end

   def *(scalar)           # To perform scalar multiplication
      Box.new(@width*scalar, @height*scalar)
   end
end

凍結物件

有時,我們希望防止物件被更改。Object 中的 freeze 方法允許我們做到這一點,有效地將物件轉換為常量。可以透過呼叫 **Object.freeze** 來凍結任何物件。凍結的物件可能不會被修改:您不能更改其例項變數。

您可以使用 **Object.frozen?** 方法檢查給定物件是否已凍結,如果物件已凍結,則返回 true,否則返回 false 值。以下示例闡明瞭這個概念:

#!/usr/bin/ruby -w

# define a class
class Box
   # constructor method
   def initialize(w,h)
      @width, @height = w, h
   end

   # accessor methods
   def getWidth
      @width
   end
   def getHeight
      @height
   end

   # setter methods
   def setWidth=(value)
      @width = value
   end
   def setHeight=(value)
      @height = value
   end
end

# create an object
box = Box.new(10, 20)

# let us freez this object
box.freeze
if( box.frozen? )
   puts "Box object is frozen object"
else
   puts "Box object is normal object"
end

# now try using setter methods
box.setWidth = 30
box.setHeight = 50

# use accessor methods
x = box.getWidth()
y = box.getHeight()

puts "Width of the box is : #{x}"
puts "Height of the box is : #{y}"

執行上述程式碼時,會產生以下結果:

Box object is frozen object
test.rb:20:in `setWidth=': can't modify frozen object (TypeError)
   from test.rb:39

類常量

您可以透過將直接數值或字串值賦值給變數來定義類中的常量,該變數是在不使用 @ 或 @@ 的情況下定義的。按照慣例,我們將常量名稱保留為大寫。

定義常量後,您無法更改其值,但您可以像變數一樣直接在類內訪問常量,但是如果您想在類外訪問常量,則必須使用 **classname::constant**,如下例所示。

#!/usr/bin/ruby -w

# define a class
class Box
   BOX_COMPANY = "TATA Inc"
   BOXWEIGHT = 10
   # constructor method
   def initialize(w,h)
      @width, @height = w, h
   end
   # instance method
   def getArea
      @width * @height
   end
end

# create an object
box = Box.new(10, 20)

# call instance methods
a = box.getArea()
puts "Area of the box is : #{a}"
puts Box::BOX_COMPANY
puts "Box weight is: #{Box::BOXWEIGHT}"

執行上述程式碼時,會產生以下結果:

Area of the box is : 200
TATA Inc
Box weight is: 10

類常量是繼承的,並且可以像例項方法一樣被重寫。

使用 Allocate 建立物件

可能存在您想要在不呼叫其建構函式 **initialize**(即不使用 new 方法)的情況下建立物件的情況,在這種情況下,您可以呼叫 *allocate*,它將為您建立一個未初始化的物件,如下例所示:

#!/usr/bin/ruby -w

# define a class
class Box
   attr_accessor :width, :height

   # constructor method
   def initialize(w,h)
      @width, @height = w, h
   end

   # instance method
   def getArea
      @width * @height
   end
end

# create an object using new
box1 = Box.new(10, 20)

# create another object using allocate
box2 = Box.allocate

# call instance method using box1
a = box1.getArea()
puts "Area of the box is : #{a}"

# call instance method using box2
a = box2.getArea()
puts "Area of the box is : #{a}"

執行上述程式碼時,會產生以下結果:

Area of the box is : 200
test.rb:14: warning: instance variable @width not initialized
test.rb:14: warning: instance variable @height not initialized
test.rb:14:in `getArea': undefined method `*' 
   for nil:NilClass (NoMethodError) from test.rb:29

類資訊

如果類定義是可執行程式碼,這意味著它們在某個物件的上下文中執行:self 必須引用某些內容。讓我們找出它是什麼。

#!/usr/bin/ruby -w

class Box
   # print class information
   puts "Type of self = #{self.type}"
   puts "Name of self = #{self.name}"
end

執行上述程式碼時,會產生以下結果:

Type of self = Class
Name of self = Box

這意味著類定義是使用該類作為當前物件執行的。這意味著元類及其超類中的方法在方法定義的執行過程中將可用。

廣告
© . All rights reserved.