12/08/2018, 17:21

Objects in Ruby

Ruby là một ngôn ngữ lập trình hướng đối tượng. Tất cả mọi thứ trong Ruby đều là đối tượng. Các đối tượng trong Ruby tồn tại trong suốt quá trình biên dịch code. Có 2 loại đối tượng là đối tượng có sẵn và đối tượng do chúng ta định nghĩa. Đối tượng có sẵn là các đối tượng đã được định nghĩa sẵn ...

Ruby là một ngôn ngữ lập trình hướng đối tượng. Tất cả mọi thứ trong Ruby đều là đối tượng. Các đối tượng trong Ruby tồn tại trong suốt quá trình biên dịch code. Có 2 loại đối tượng là đối tượng có sẵn và đối tượng do chúng ta định nghĩa. Đối tượng có sẵn là các đối tượng đã được định nghĩa sẵn trong Ruby hoặc các thư viện do người khác viết bằng Ruby và chúng ta có thể sử dụng. Còn loại kia là đối tượng do chúng ta định nghĩa để sử dụng với công việc đặc thù của riêng chúng ta khi các đối tượng có sẵn không đáp ứng được.

Muốn sử dụng đối tượng thì chúng ta phải khởi tạo trước. Một đối tượng chứa thuộc tính và phương thức bên trong nó. Thuộc tính là dữ liệu của đối tượng, đây là thành phần tĩnh, phương thức là phần động. Đối tượng có thể được chỉnh sửa hoặc giao tiếp với các đối tượng khác thông qua phương thức.

puts "Ruby language"

Nếu bạn đã từng lập trình C++, Java thì bạn cũng đã biết tới những từ khóa hoặc hàm dùng để in một chuỗi ra màn hình, chẳng hạn như hàm printf() trong C, std::cout trong C++, System.out.println() trong Java… các hàm này có chức năng là in một chuỗi ra màn hình, từ khóa puts trong Ruby ở trên cũng có chức năng tương tự. Các từ khóa/hàm này nhận vào một giá trị là một chuỗi, trong ví dụ trên thì “Ruby language” là một giá trị,

Nhưng trong Ruby thì có hơi khác, bản thân chuỗi “Ruby language” cũng là một đối tượng. Do đó chúng ta cũng có thể gọi phương thức từ chuỗi này.

Ngoài ra phương thức tồn tại trong các đối tượng chứ không tự tồn tại một mình ở đâu đó được, puts cũng là một phương thức của một đối tượng đó là module Kernel.

Ví dụ 1:

simple2.rb
Kernel.puts "Ruby language"
Kernel.puts "Ruby language".size

Trong dòng code đầu tiên chúng ta gọi phương thức puts và gọi cả tên module Kernel ra luôn. Trong C# hay Java cũng tương tự là Console.writeln và System.out.println(). Tất nhiên là chúng ta cũng không cần gọi Kernel ra như vậy trong Ruby nhưng ví dụ này chỉ để khẳng định là phương thức thì luôn đi kèm với một đối tượng nào đó.

Trong dòng tiếp theo chúng ta in ra kích thước của chuỗi “Ruby language”, điều này chứng tỏ chuỗi “Ruby language” cũng chính là một đối tượng chứ không đơn thuần chỉ là một giá trị như trong các ngôn ngữ khác. Phương thức size sẽ trả về số lượng kí tự trong một chuỗi.

Output
Ruby language
13

Ví dụ 2:

objectnumber.rb

puts 6.object_id
 
puts 6.even?
puts 6.zero?
 
puts 6.class

Chúng ta có số 6 và cũng có thể gọi một số phương thức của số 6 này.

puts 6.object_id

Mỗi đối tượng đều được gán một id và chúng ta có thể lấy giá trị đó ra bằng cách gọi phương thức object_id. Mỗi lời gọi phương thức đều được đặt sau dấu chấm sau tên đối tượng.

puts 6.even?
puts 6.zero?

Phương thức even? trả về True nếu số đó là số chẵn và ngược lại. Phương thức zero? trả về True nếu số đó là 0. Ở đây có một điểm lưu ý là nếu phương thức trả về True hoặc False thì phải kèm thêm dấu ? sau tên phương thức.

puts 6.class

Phương thức class cho biết tên lớp của đối tượng, ở đây số 6 là một đối tượng thuộc lớp Fixnum.

Output
13
true
false
Fixnum

Tạo đối tượng

Một đối tượng phải được tạo ra thì mới có thể sử dụng được. Đối tượng trong Ruby có thể được tạo ra một cách rõ ràng hoặc tạo ngầm. Ví dụ về đối tượng ngầm là mấy cái đối tượng được tạo ra từ giá trị như số 6 hay chuỗi “Ruby language” ở trên. Còn để tạo đối tượng một cách rõ ràng thì chúng ta dùng từ khóa new. Đối tượng thuộc lớp do chúng ta tự định nhĩa cũng dùng từ khóa new.

object_creation.rb

class Being
end
     
puts 67
puts "Hello world"
 
s = String.new "Hello world"
puts s
 
# n1 = Fixnum.new 67
# puts n1
 
b = Being.new
puts b

Trong đoạn code trên chúng ta tạo một số đối tượng bằng các cách khác nhau.

class Being
end

Chúng ta định nghĩa lớp Being. Để định nghĩa một lớp thì chúng ta dùng từ khóa class.

puts 67
puts "Hello world"

Hai dòng code trên tạo đối tượng ngầm là 67 và chuỗi “Hello world”.

s = String.new "Hello world"
puts s

Tương tự, chúng ta có thể tạo đối tượng string một cách rõ ràng luôn với từ khóa new.

# n1 = Fixnum.new 67
# puts n1

Không phải tất cả các lớp có sẵn trong Ruby đều có thể được tạo ra bằng từ khóa new, Fixnum là một ví dụ, các đối tượng Fixnum chỉ có thể được tạo ngầm.

b = Being.new
puts b

Đoạn code trên tạo đối tượng b từ lớp Being do chúng ta định nghĩa.

Output

67
Hello world
Hello world
#<Being:0x8492o3a>

Đối tượng giá trị

Như đã nói ở trên là chúng ta có thể tạo đối tượng một cách ngầm định từ các giá trị. Ở đây chúng ta sẽ làm việc với một số phương thức của loại đối tượng này.

literals.rb

4.times { puts "Ruby" }
 
puts "Ruby".size
puts "Ruby".downcase
 
puts [1, 2, 3].include? 3
puts [1, 2, 3].empty?
 
puts :name.class
puts :name.frozen?
 
puts (1..6).class
puts (1..6).include? 4

Trong đoạn code trên chúng ta có các đối tượng Fixnum, String, Array, kiểu Symbol và kiểu Range. Chúng ta sẽ tìm hiểu kỹ hơn về các kiểu đối tượng này trong bài sau.

4.times { puts "Ruby" }

Phương thức times của đối tượng Fixnum thực hiện phép nhân lên giá trị phía sau nó, ở đây là thực hiện puts "Ruby" 4 lần.

puts "Ruby".size
puts "Ruby".downcase

Phương thức size lấy về số kí tự trong chuỗi, phương thức downcase thực hiện chuyển chuỗi về kiểu chữ in thường.

puts [1, 2, 3].include? 3
puts [1, 2, 3].empty?

Trong 2 dòng code trên chúng ta sử dụng đối tượng kiểu mảng – Array, phương thức include? cho biết một giá trị nào đó có thuộc mảng hay không. Phương thức empty? cho biết mảng này rỗng hay không.

puts :name.class
puts :name.frozen?

Ở 2 dòng trên chúng ta thao tác với kiểu Symbol, symbol có tên bắt đầu bằng dấu 2 chấm ":" chúng ta sẽ tìm hiểu thêm trong bài sau.

puts (1..6).class
puts (1..6).include? 4

Cuối cùng là 2 đối tượng kiểu Range, về bản chất thì kiểu này cũng tương tự như kiểu mảng vậy. Phương thức class trả về tên của kiểu dữ liệu này, còn phương thức include? cho biết giá trị nào đó có nằm trong danh sách hay không.

Output
Ruby
Ruby
Ruby
Ruby
4
ruby
true
false
Symbol
false
Range
true

Thừa kế

Một trong những khái niệm trong lập trình hướng đối tượng là thừa kế và Ruby cũng thế. Thừa kế bao gồm các đối tượng cha và đối tượng con, đối tượng cha sẽ chứa những thuộc tính và phương thức mà đối tượng con có thể thừa kế lại. Tất cả các đối tượng trong Ruby đều thừa kế từ một đối tượng gốc có tên là Object. Tất cả các đối tượng trong Ruby đều có các phương thức mà đối tượng Object có, nhưng có thể định nghĩa lại.

mother.rb
puts 4.is_a? Object
puts "Ruby".is_a? Object
puts [2, 3].is_a? Object
puts :name.is_a? Object
puts (1..2).is_a? Object

Đoạn code trên cho chúng ta thấy tất cả các đối tượng đều thừa kế từ đối tượng Object.

puts 4.is_a? Object

Trong dòng code trên phương thức is_a? cho biết đối tượng 4 có được kế thừa từ đối tượng Object hay không.

true
true
true
true
true

Trong ví dụ dưới đây, chúng ta sẽ định nghĩa các lớp có kế thừa nhau.

custom_inher.rb
class Being
  def to_s
      "This is Being"
   end
   def get_id
      9
   end
end
 
class Living < Being
    def to_s
        "This is Living"
    end
end
l = Living.new
 
puts l
puts l.get_id
puts l.is_a? Being

Chúng ta định nghĩa 2 lớp Being và Living, lớp Living kế thừa từ lớp Being, do đó lớp Being là lớp cha, lớp Living là lớp con.

class Being 
   def to_s
     "This is Being"
   end
   def get_id
      9
   end
end

Để định nghĩa một lớp thì chúng ta sử dụng cặp từ khóa class...end, sau từ khóa class là tên lớp mà chúng ta muốn đặt. Bên trong lớp chúng ta có thể định nghĩa các phương thức bằng cách dùng cặp từ khóa def…end, sau từ khóa def cũng từ tên phương thức mà chúng ta muốn đặt. Trong đoạn code trên chúng ta có 2 phương thức là to_s và get_id.

Đối tượng nào cũng có phương thức to_s cả vì mặc định đối tượng Object cũng có phương thức này. Khi chúng ta dùng phương thức puts để in ra một giá trị nào đó thì phương thức này sẽ tự động gọi đến phương thức to_s có trong các đối tượng. Trong ví dụ này chúng ta định nghĩa lại phương thức to_s, nếu không thì puts sẽ sử dụng phương thức to_s mặc định của đối tượng Object.

class Living < Being
 def to_s
   "This is Living"
  end
end

Chúng ta định nghĩa lớp Living, lớp này kế thừa từ lớp Being, để một lớp được kế thừa từ lớp khác thì chúng ta thêm dấu < cùng với tên của lớp cha vào sau tên lớp con. Ở đây lớp Living cũng định nghĩa lại phương thức to_s của riêng nó.

l = Living.new

Chúng ta tạo đối tượng Living bằng cách dùng từ khóa new.

puts l

Phương thức puts sẽ gọi đến phương thức to_s trong lớp Living.

puts l.get_id

Lớp Living không định nghĩa lại phương thức get_id nên Ruby sẽ tìm dần dần các lớp cha xem lớp nào có thì gọi phương thức get_id từ lớp đó.

puts l.is_a? Being

Dòng code trên sẽ trả về True vì lớp Living kế thừa từ lớp Being nên một đối tượng Living cũng là một đối tượng Being.

Output
This is Living
true

Đối tượng main

Trong các ngôn ngữ như C++, Java… thì chương trình bắt đầu chạy từ một hàm đặc biệt tên là hàm main(), khi chạy chương trình hàm này sẽ được gọi từ hệ điều hành.

Trong Ruby cũng thế, mỗi đoạn code trong file .rb đều nằm trong một đối tượng có tên là main mặc dù ở đây chúng ta không khai báo ra, đối tượng này cũng kế thừa từ đối tượng Object. Chính vì main cũng là một đối tượng nên các biến được khai báo trong main cũng là thuộc tính của main.

toplevel.rb
n1 = 3
n2 = 5

 
puts local_variables
 
Kernel.puts self
puts self.class

Đoạn code trên ví dụ về đối tượng main trong Ruby.

n1 = 3
n2 = 5

Chúng ta có 2 đối tượng số nguyên. Cả hai đối tượng này đều thuộc về đối tượng main.

puts local_variables

Biến local_variables thực chất là một phương thức của module Kernel, lưu trữ danh sách các biến cục bộ hiện có.

Kernel.puts self

Biến self là biến tham chiếu đến đối tượng hiện tại, nói cho dễ hiểu thì self chính là main, nếu bạn biết con trỏ this trong C++, Java… thì self cũng giống như con trỏ this vậy.

puts self.class

Biến class cho biết tên lớp của đối tượng hiện tại, hiện tại chúng ta dùng đối tượng self (hoặc main) mà đây là đối tượng thuộc lớp Object nên dòng trên sẽ in ra chuỗi Object.

Output
n1
n2
main
Object
0