06/04/2021, 14:47

Cách tạo Method - Class và Object trong Ruby - Ruby căn bản

Trong bài này mình sẽ hướng dẫn cách tạo Method - Class và Object trong Ruby dành cho người mới bắt đầu học lập trình Ruby. Ở các bài trước chúng ta đã đi lòng vòng để làm quen với Ruby nhưng chưa đụng đến code nhiều. Bắt đầu từ bài này trở đi thì mình sẽ đi sâu vào chi tiết bên trong của ngôn ...

Trong bài này mình sẽ hướng dẫn cách tạo Method - Class và Object trong Ruby dành cho người mới bắt đầu học lập trình Ruby.

Ở các bài trước chúng ta đã đi lòng vòng để làm quen với Ruby nhưng chưa đụng đến code nhiều. Bắt đầu từ bài này trở đi thì mình sẽ đi sâu vào chi tiết bên trong của ngôn ngữ này nhiều hơn.

Trong bài này mình sẽ nói về khái niệm tạo hàm, gọi hàm, cách tạo class và khởi tạo đối tượng trong Ruby. OK! bây giờ thì hãy đi uống một cốc nước để lấy bình tĩnh hoặc có thể đem 1 tách cafe để bắt đầu một cuộc hành trình vào thế giới Ruby đi nào :)

1. Hàm trong Ruby (Method)

Cú pháp

Việc tạo hàm trong Ruby khá đơn giản, với cú pháp như sau:

Tạo hàm trong ruby
def ten_ham (bien1, bien2)
# Todo something
end

def được viết tắt bởi từ "define" có nghĩa là khai báo một hàm với tên phương thức và các biến số truyền vào trong hàm. Các biến số này có thể có hoặc không.

Cách gọi hàm thì gọi ra đúng tên hàm mình đã khai báo, biến số có thể truyền vào hoặc không tuỳ theo hàm mà mình đã khai báo. Ví dụ: ten_ham(bien1, bien2)

Các lưu ý

Cú pháp trong ruby cần phải được lưu ý như sau:

Thứ nhất, khi kết thúc 1 dòng lệnh thì không cần dấu "chấm phẩy" như ngôn ngữ PHP hay Javascript. Ví dụ

Ví dụ 1
def tong_hai_so (a, b)
  puts "Tổng 2 số là:"
  tong = a + b
  puts tong
end

Thứ hai, không cần return về giá trị cuối cùng của hàm, mặc định hàm trong Ruby thì giá trị cuối cùng sẽ tự động được return. Đối với javascript hay php thì khi khai báo 1 hàm thì phải có giá trị trả về của hàm bằng từ khoá return, còn Ruby thì không cần. Ví dụ:

Ví dụ 2
def tong_hai_so (a, b)
  a + b
  a - b # dòng này sẽ được return về, bởi vì nó là giá trị cuối cùng được trả về trong hàm
end

Khi gọi hàm tong_hai_so(10, 5) thì kết quả sẽ là 10 - 5 = 5, không phải 10 + 5 = 15. Bởi vì giá trị cuối cùng trả về của hàm là 10 - 5 (tức a - b). Đây là điểm mạnh của Ruby so với các ngôn ngữ khác.

Thứ ba, dùng từ khoá "return" trong hàm khi ta muốn trả về một giá trị bất kỳ. Ví dụ:

Ví dụ 3:
def tong_hai_so (a, b)
  return a + b
  a - b # dòng này sẽ không được trả về bởi vì nằm sau từ khoá return
end

Khi gọi hàm tong_haI_so(10, 5) thì kết quả sẽ là 10 + 5 = 15, không phải 10 - 5 = 5. Bởi vì ta dùng từ khoá return tại dòng a + b, khi gặp từ khoá return thì hàm sẽ tính toán và dừng lại, những dòng code bên dưới lệnh return sẽ không thực thi. Nếu bạn nào đã từng học về lập trình thì sẽ thấy cách hoạt động của return trong Ruby củng giống như các ngôn ngữ khác.

Thứ tư, khi gọi hàm và truyền biến vào trong hàm ta có thể có hoặc không dấu ngoặc tròn () để chứa biến. Ví dụ ta gọi hàm bằng 2 cách sau đều được:

Ví dụ 4
tong_hai_so(10, 5)

# hoặc

tong_hai_so 10, 5

Thứ năm, khai báo giá trị mặc định cho biến trong hàm

Ví dụ 5
def tong_hai_so (a = 0, b = 0)
  a + b
end

Ở trên ta đã khai báo giá trị mặc định cho biến a và b, cho nên khi gọi hàm tong_hai_so ta có thể truyền biến hoặc không truyền biến hàm vẫn chạy bình thường không có lỗi. Ở các ví dụ trên buộc ta phải truyền hai biến a và b nếu không hàm sẽ báo lỗi "wrong number of arguments"

2. Lớp và đối tượng trong Ruby (Class - Object)

Cú pháp

Class trong Ruby được khai báo như sau:

Ví dụ 6
class ten_lop
 # todo something
end

Trong mỗi class sẽ có hàm khởi tạo, với php hàm khởi tạo là _construct còn với ruby hàm khởi tạo là initialize. Ví dụ:

Ví dụ 7
class ten_lop
  def initialize
    #todo something
  end
end

Thông thường hàm khởi tạo thường được dùng đễ tạo biến, gán biến, định dạng param. Ví dụ như truyền vào 1 số nguyên, thì ở hàm khởi tạo ta sẽ ép kiểu dữ liệu cho nó rồi trong các method ta sẽ sử dụng biến đã được ép kiểu để tính toán.

Bài tập làm quen

Ta có một bài tập nhỏ để làm quen với việc sử dụng Class trong Ruby như sau:

Yêu cầu: Tạo ra 1 class DongVat, trong class chứa 3 con vật là chó, mèo, vịt. Mỗi con vật sẽ phát ra 1 tiếng kêu khác nhau. Khi ta gọi con chó sẽ kêu "Go Go", khi ta gọi con mèo sẽ kêu "mew mew", khi ta gọi con vịt sẽ kêu "duck duck".

Ta làm như sau: Tạo 1 file dong_vat.rb với nội dung như sau

Ví dụ 8
class DongVat
  def initialize
    puts 'Xin chào bạn!'
  end

  def dog
    'Tiếng kêu của con chó là: Go Go'
  end

  def cat
    'Tiếng kêu của con mèo là: Mew Mew'
  end

  def duck
    'Tiếng kêu của con vịt là: Duck Duck'
  end
end

thu_nuoi = DongVat.new
puts thu_nuoi.dog # Xuất ra màn hình tiếng kêu của con chó
puts thu_nuoi.cat # Xuất ra màn hình tiếng kêu của con mèo
puts thu_nuoi.duck # Xuất ra màn hình tiếng kêu của con vịt

Sau khi đã tạo file dong_vat.rb với nội dung như trên, giờ hãy mở terminal và chạy file với cú pháp ruby dong_vat.rb. Kết quả xuất hiện trên màn hình là

Cách tạo class trong Ruby

Ta khai báo thu_nuoi = DongVat.new .Ta gọi thu_nuoi là một đối tượng hay còn gọi là Object. Vậy cú pháp tạo một đối tượng là doi_tuong = TenClass.new

Phương thức new là phương thức được định sẵn và duy nhất trong class. Nó phương thức thuộc về Class method. Đối với phương thức nào được gọi ra bởi class thì được gọi là Class method.

Tiếp theo, dòng puts đầu tiên xuất hiện "Xin chào bạn". Nguyên nhân do trong hàm khởi tạo initialize ta có puts ra màn hình câu "Xin chào bạn" nên khi ta khởi tạo 1 đối tượng DongVat.new thì hàm initialize sẽ được thực thi.

Ba dòng tiếp theo sẽ in ra màn hình theo tiếng gọi của từng con vật mà ta đã khai báo ở trên thông qua đối tượng thu_nuoi. Và các phương thức này được gọi đến thông qua một đối tượng, vậy chúng ta gọi các phương thức này là instance method

Các loại phương thức trong class

Đối với phương thức trong class của Ruby có 2 loại: 1 là instance method, 2 là class method

A. Instance method

Hiểu nôm na là method được gọi từ 1 đối tượng. Như ví dụ lớp động vật ở trên, ta có các phương thức dog, cat, duck và các phương thức này được gọi ra thông qua đối tượng là thu_nuoi. Vậy ta kết luận rằng các phương thức dog, cat, duck này là instance method

B. Class method

Hiểu nôm na là method được gọi từ 1 class. Ví dụ: ta thêm 1 class method people vào trong lớp DongVat

Ví dụ 7
class DongVat
  def initialize
    puts 'Xin chào bạn!'
  end

  def dog
    'Tiếng kêu của con chó là: Go Go'
  end

  def cat
    'Tiếng kêu của con mèo là: Mew Mew'
  end

  def duck
    'Tiếng kêu của con vịt là: Duck Duck'
  end

  # Từ khoá self: đại diện cho tên class là DongVat
  # Có thể dùng DongVat.people
  def self.people
    'Give me some money'
  end
end

thu_nuoi = DongVat.new
puts thu_nuoi.dog
puts thu_nuoi.cat
puts thu_nuoi.duck
puts DongVat.people

Ta thấy ở hàm people ta dùng từ khoá self đại diện cho tên class DongVat hoặc ta có thể dùng tên class DongVat.ten_method để tạo ra class method.

Cách gọi class method với cú pháp TenClass.ten_class_method

Khi chạy ra màn hình ta sẽ thấy xuất hiện dòng chữ "'Give me some money'" từ việc gọi trực tiếp từ Class mà không cần thông qua đối tượng.

Tóm lại: Việc sử dụng class method hay instance method tuỳ thuộc vào ý đồ của người lập trình viên, nhưng các bạn nên phân biệt được đâu là class method và đâu là instance method.

Tóm tắt nội dung

Vậy ta có thể hiểu tóm gọn như sau:

  • Để tạo ra một lớp ta dùng từ khoá class
  • Hàm initialize là hàm khởi tạo của class. Hàm sẽ được thực thi khi ta khởi tạo 1 đối tượng
  • Để tạo ra một đối tượng ta dùng cú pháp: doi_tuong = TenClass.new
  • Phương thức new là phương thức thuộc về Class method
  • Để truy xuất đến các phương thức trong class thì ta phải tạo ra một đối tượng và dùng dấu chấm + tên phương thức. Ví dụ: doituong.ten_phuong_thuc_trong_class
  • Instance method là method được gọi từ 1 đối tượng
  • Class method là method được gọi từ 1 class

Các bạn có thể tham khảo thêm bài viết class trong PHP nó củng tương tự như trong Ruby.

3. Tính kế thừa trong Ruby

Tính kế thừa trong Ruby hay trong bất kỳ ngôn ngữ nào củng cùng một tư tưởng thiết kế như nhau. Kế thừa là một kỹ thuật mà trong đó một đối tượng thu được tất cả thuộc tính và hành vi của đối tượng cha. Khi bạn kế thừa từ một lớp đang tồn tại, bạn có thể tái sử dụng các phương thức và các trường của lớp cha, và bạn cũng có thể bổ sung thêm các phương thức và các trường khác. Tính kế thừa biểu diễn mối quan hệ IS-A, còn được gọi là mối quan hệ cha-con.

Ta có ví dụ về tính kế thừa trong Class như sau: Tạo một file dong_vat.rb với nội dung

Tính kế thừa class trong Ruby
class ThuocTinh
  def dong_vat_4_chan
    'Là loài động vật có 4 chân'
  end

  def dong_vat_2_chan
    'Là loài động vật có 2 chân'
  end
end

class DongVat < ThuocTinh
  def initialize
    puts 'Xin chào bạn!'
  end

  def dog
    'Tiếng kêu của con chó là: Go Go' + '. ' + dong_vat_4_chan
  end

  def cat
    'Tiếng kêu của con mèo là: Mew Mew' + '. ' + dong_vat_4_chan
  end

  def duck
    'Tiếng kêu của con vịt là: Duck Duck' + '. ' + dong_vat_2_chan
  end
end

thu_nuoi = DongVat.new
puts thu_nuoi.dog
puts thu_nuoi.cat
puts thu_nuoi.duck

Đầu tiên ta có class ThuocTinh, trong đó có 2 method là dong_vat_4_chan và dong_vat_2_chan. Tiếp theo là class DongVat kế thừa từ class ThuocTinh. Và cú pháp kế thừa thông qua dấu "<" ; khá đơn giản và dễ hiểu phải không nào :)

Class DongVat kế thừa class ThuocTinh (class DongVat < ThuocTinh), ta gọi class DongVat là class con - class ThuọcTinh là class cha.

Trong class DongVat, ở mỗi phương thức mình có gọi đến phương thức dong_vat_4_chan và dong_vat_2_chan của class cha, và mình có sử dụng dấu + để nối chuỗi với nhau.

Ta tạo ra một đối tượng thu_nuoi và gọi đến từng method trong class DongVat. Khi ta chạy dòng lệnh ruby dong_vat.rb trên màn hình terminal thì nội dung sẽ là

Kế thừa class trong Ruby

Qua kết quả trên màn hình thì ta có thể hình dung và kết luận rằng, sau khi kế thừa thì tất cả những phương thức của class cha thì class con sẽ được thừa hưởng hoàn toàn. Bây giờ ta có một câu hỏi đặt ra rằng, nếu đã kế thừa được thì có thể ghi đè được không? Câu trả lời hoàn toàn có thế được, với ví dụ như sau các bạn sẽ dễ dàng hình dung hơn.

Overwrite phương thức
class ThuocTinh
  def dong_vat_2_chan
    'Là loài động vật có 2 chân'
  end

  def tinh_cach
    'Là loài động vật giữ nhà'
  end
end

class DongVat < ThuocTinh
  def dog
    'Tiếng kêu của con chó là: Go Go' + '. ' + dong_vat_2_chan + '. ' + tinh_cach
  end

  # Overwrite phương thức tinh_cach
  def tinh_cach
    'Là loài động vật đi rong, nuôi lớn làm thịt'
  end

  def duck
    'Tiếng kêu của con vit là: Duck Duck' + '. ' + dong_vat_2_chan + '. ' + tinh_cach
  end
end

thu_nuoi = DongVat.new
puts thu_nuoi.dog
puts thu_nuoi.duck

Nội dung ví dụ củng tương tự như trên, ta thấy trong class ThuocTinh có phương thức tinh_cach. class DongVat kế thừa class ThuocTinh và có ghi đè (overwrite) lại phương thức tinh_cach ở class cha. Sau khi chạy trên terminal ta thấy nội dung như sau:

overwrite method

Với kết quả trên màn hình thì đã quá rõ rồi, có lẽ mình sẽ không giải thích gì thêm. Nhưng trong ví dụ này mình đã cố tình đưa phương thức tinh_cach ra sau phương thức dog, mục đích để các bạn thấy rằng khi ta overwrite phương thức thì nó không quan trọng thứ tự trước sau.

Tiếp tục, ta lại có một câu hỏi khó hơn là làm thế nào để có thể vừa sử dụng kết quả của phương thức cha mà củng vừa có thể overwrite lại phương thức cha. WTF!!! Nghe bất hợp lý quá, nghe cứ giống như là đang học triết học vậy. Nhưng đối với Ruby thì không gì là không thể :D, ta xem xét ví dụ dưới đây để xem nó bất hợp lý không

super method
class ThuocTinh
  def dong_vat_2_chan
    'Là loài động vật có 2 chân'
  end

  def tinh_cach
    'Là loài động vật giữ nhà'
  end
end

class DongVat < ThuocTinh
  def dog
    'Tiếng kêu của con chó là: Go Go' + '. ' + dong_vat_2_chan + '. ' + tinh_cach
  end

  # Overwrite phương thức tinh_cach
  def tinh_cach
    super + '. ' + 'Giữ nhà không được thì xin mời lên mâm'
  end

  def duck
    'Tiếng kêu của con vit là: Duck Duck' + '. ' + dong_vat_2_chan + '. ' + tinh_cach
  end
end

thu_nuoi = DongVat.new
puts thu_nuoi.dog
puts thu_nuoi.duck

Kết quả sẽ hiển thị ra như bên dưới:

super method

Ở trong phương thức tinh_cach của class DongVat mình có sử dụng từ khoá super, về bản chất super củng chỉ là một hàm mà thôi, không phải từ khoá gì, bởi vì Ruby có thể viết hàm và truyền biến vào hàm mà không cần dùng dấu (), cho nên các bạn sẽ dễ bị nhầm lần, bây giờ mình sẽ không dùng chữ từ khoá mà mình sẽ nói super là hàm nhé, các bạn đừng nghĩ nó là từ khoá hay gì nha, đơn giản hoá vấn đề thì chúng ta sẽ dễ hiểu hơn.

Quay lại vấn đề hàm super trong phương thức thì ta hiểu như sau: Khi ta gọi super ở vị trí nào thì nó sẽ thực thi và lấy toàn bộ giá trị ở phương thức trên nó (tức là phương thức cha) lưu trả về cho hàm super, và ta có thể đem giá trị trong hàm super này làm những việc khác.

4. Kết luận

Trong bài nay mình muốn các bạn biết được cách tạo hàm, gọi hàm, phân biệt được thế nào là đối tượng và lớp, cách khai báo lớp, cách truy xuất các thuộc tính và phương thức của lớp trong Ruby để qua những bài sau dễ dàng hiểu các ví dụ mình đưa ra hơn. Bài tiếp theo chúng ta sẽ tìm hiểu về các loại biến và module trong Ruby.

Bài tiếp

Tạ Quốc Bảo

23 chủ đề

7270 bài viết

0