Cách tạo Method - Class và Object trong Ruby
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:
Bài viết này được đăng tại [free tuts .net]
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ụ
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ụ:
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ụ:
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:
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
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:
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ụ:
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
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à
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
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
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à
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.
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:
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
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:
Ở 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.