Ruby – Module và Exception

4.8/5 - (63 votes)

Trong phần này chúng ta sẽ tìm hiểu về module và exception trong Ruby.

Module

Một module là một tập các phương thức, lớp, hằng số, do đó module cũng gần giống như lớp vậy, chỉ khác là module không thể tạo các đối tượng và không thể thừa kế.

Thường thì chúng ta sẽ gộp các lớp, phương thức và hằng số có liên quan với nhau vào một module để tránh xung đột tên. Nếu bạn đã từng làm việc với C# và Java thì có thể nói module trong Ruby tương đương với namespace trong C# và package trong Java vậy.

Ngoài ra chúng ta còn có thể sử dụng module trong Ruby để thực hiện đa thừa kế nữa.

Ví dụ 1:

puts Math::PI
puts Math.sin 2

Math là một module có sẵn trong Ruby, module này chứa các hằng số và phương thức hỗ trợ thực hiện các phép tính toán học. Ở đoạn code trên chúng ta in ra hằng số PI và dùng phương thức sin.

include Math

puts PI
puts sin 2

Nếu không muốn ghi tên module ra trực tiếp thì chúng ta có thể sử dụng từ khóa include Math để báo cho Ruby biết là chúng ta sẽ dùng module Math.

3.141592653589793
0.9092974268256817

Ví dụ 2:

module Forest
 
    class Rock ; end
    class Tree ; end
    class Animal ; end    
    
end

module Town
    
   class Pool ; end
   class Cinema ; end
   class Square ; end
   class Animal ; end
        
end


p Forest::Tree.new
p Forest::Rock.new
p Town::Cinema.new

p Forest::Animal.new
p Town::Animal.new

Để định nghĩa một module thì chúng ta dùng cặp từ khóa module…end, tên module được đặt sau từ khóa module.

module Forest
 
    class Rock ; end
    class Tree ; end
    class Animal ; end    
    
end

Thường thì chúng ta sẽ nhóm những thứ có liên quan với nhau lại vào trong một module. Như trong đoạn code trên chúng ta định nghĩa các lớp Rock (đá), Tree (cây) và Animal (động vật) vào trong module Forest (rừng).

p Forest::Tree.new
p Forest::Rock.new
p Town::Cinema.new

Để truy xuất một đối tượng trong một module thì chúng ta dùng toán tử ::

p Forest::Animal.new
p Town::Animal.new

Như bình thường thì chúng ta không thể định nghĩa 2 lớp có tên giống nhau trong một file được. Nhưng ở đây chúng ta sử dụng thêm cả module nữa nên có thể định nghĩa 2 lớp có tên giống nhau trong một file.

#<Forest::Tree:0x97f35ec>
#<Forest::Rock:0x97f35b0>
#<Town::Cinema:0x97f3588>
#<Forest::Animal:0x97f3560>
#<Town::Animal:0x97f3538>

Ví dụ 3:

Hướng đối tượng trong C++ có hỗ trợ đa thừa kế, trong Java cũng hỗ trợ đa thừa kế thông qua Interface, còn Ruby hỗ trợ đa thừa kế thông qua module.

module Device
    def switch_on ; puts "on" end    
    def switch_off ; puts "off" end
end

module Volume
    def volume_up ; puts "volume up" end    
    def vodule_down ; puts "volume down" end
end

module Pluggable
    def plug_in ; puts "plug in" end    
    def plug_out ; puts "plug out" end
end

class CellPhone
    include Device, Volume, Pluggable
   
    def ring
        puts "ringing"
    end    
end

cph = CellPhone.new
cph.switch_on
cph.volume_up
cph.ring

Đa thừa kế tức là một lớp có thể thừa kế từ nhiều lớp khác, chúng ta có thể sử dụng module của Ruby để thực hiện đa thừa kế.

Ở đây chúng ta định nghĩa một lớp, sau đó include các module bên trong lớp này và lớp đó sẽ có thể sử dụng những phương thức, hằng số… của các module đã được include.

class CellPhone
    include Device, Volume, Pluggable
   
    def ring
        puts "ringing"
    end    
end

Trong ví dụ này chúng ta có 3 module Device, VolumePluggable, mỗi module có một số phương thức in chuỗi. Chúng ta cũng định nghĩa lớp CellPhone và include cả 3 module trên vào.

cph = CellPhone.new
cph.switch_on
cph.volume_up
cph.ring

Như thế khi tạo đối tượng lớp CellPhone, chúng ta có thể gọi luôn cả các phương thức của những module mà lớp đó đã include chứ không cần gọi đến tên module nữa.

on
volume up
ringing

Exception

Trong lập trình có 3 loại lỗi là lỗi biên dịch, lỗi ngữ nghĩa và lỗi exception – lỗi ngoại lệ. Lỗi exception là lỗi xảy ra trong quá trình chương trình chạy (tiếng anh là run time error). Ví dụ như bạn viết một chương trình máy tính bỏ túi gồm các chức năng cộng trừ nhân chia. Thoạt nhìn có vẻ như rất đơn giản, chỉ cần yêu cầu người dùng nhập vào 2 số và phép tính, thực hiện phép tính rồi trả lại kết quả cho người dùng. Nhưng chương trình này sẽ báo lỗi nếu người dùng thực hiện một phép tính chia với số chia là 0. Đó là loại lỗi chỉ xảy ra trong khi chương trình chạy, để phòng ngừa loại lỗi này thì chỉ có một cách là các lập trình viên phải đoán trước các trường hợp lỗi ngoại lệ có thể xảy ra và xử lý chúng trước trong code của mình. Nhưng vì bạn chẳng phải nhà tiên tri, bạn không thể nào đoán trước tất cả mọi thứ được ? nên trên thực tế thì các phần mềm lớn vẫn thường có lỗi chứ không có phần mềm nào không có lỗi cả, công việc của chúng ta là cố gắng phát hiện lỗi và sửa lỗi thôi.

Trong Ruby các lỗi exception thường gặp được định nghĩa thành một lớp riêng, và tất cả chúng đều được kế thừa từ lớp Exception. Ngoài ra chúng ta cũng có thể định nghĩa lớp exception của riêng chúng ta.

Ví dụ 1:

x = 35
y = 0

begin
    z = x / y
    puts z
rescue => e
    puts "Error: #{e}"
end

Trong ví dụ này chúng ta sẽ thử thực hiện một phép chia với mẫu số là 0.

begin
    z = x / y
    puts z

Những câu lệnh mà có khả năng giải phóng exception sẽ được đặt trong cặp từ khóa begin...end.

rescue => e
    puts "Error: #{e}"
end

Để có thể bắt lỗi thì chúng ta dùng từ khóa rescue theo sau là dấu => rồi đến tên tham số đối tượng exception. Các đối tượng exception sẽ chứa một chuỗi mô tả về tên lỗi exception đã xảy ra. Chúng ta có thể dùng phương thức puts để in đoạn chuỗi đó ra.

Error: divided by 0

Ví dụ 2:

age = 17

begin
    if age < 18 
        raise "Under 18 is not allowed" 
    end 

    puts "Allowed" 
rescue => e
    puts e
    p e    
end

Chúng ta có thể tự giải phóng một lỗi exception bằng cách dùng từ khóa raise.

begin
    if age < 18
        raise "Under 18 is not allowed"
    end
 
    puts "Allowed"

Theo sau từ khóa raise chúng ta đưa vào tham số là một chuỗi, Ruby sẽ tạo một đối tượng RuntimeException và truyền vào đó chuỗi này. Khi một exception được giải phóng, chương trình sẽ bị ngắt ngay tại đó.

rescue => e
    puts e
    p e    
end

Nếu không có câu lệnh rescue để bắt lỗi thì chương trình sẽ thoát luôn, còn ở đây chúng ta có câu lệnh bắt lỗi nên chương trình sẽ tiếp tục thực thi những gì có trong câu lệnh này.

Nếu như phương thức puts chỉ in đoạn chuỗi mô tả lỗi thì phương thức p sẽ in cả tên lớp exception và đoạn chuỗi mô tả.

Under 18 is not allowed
#<RuntimeError: Under 18 is not allowed>

Ví dụ 3:

age = 17

begin
    if age < 18 
        raise "Under 18 is not allowed" 
    end 
    puts "Entry allowed" 
rescue => e
    puts e
    p e
ensure
    exit 0
end

Ruby còn có từ khóa ensure, những câu lệnh nằm sau từ khóa ensure sẽ được thực thi cho dù có xảy ra lỗi hay không. Từ khóa ensure có tác dụng giống như từ khóa finally trong các ngôn ngữ như C#, Java…

ensure
    exit 0
end

Chúng ta thực thi câu lệnh exit để thoát chương trình trong từ khóa ensure, khi chương trình chạy, cho dù có lỗi xảy ra hay không thì câu lệnh exit cũng sẽ được thực thi, mặc dù ở đây câu lệnh này cũng không có ý nghĩa mấy vì dù gì chương trình cũng tự thoát.

Ví dụ 4:

age = 17

class NotOver18Exception < StandardError
end

begin
    if age < 18 
        raise NotOver18Exception, "Under 18 is not allowed" 
    end 
    puts "Entry allowed" 
rescue => e
    puts e
    p e
ensure
    exit 0
end

Chúng ta có thể định nghĩa lớp exception của riêng chúng ta và giải phóng đối tượng thuộc lớp đó.

class NotOver18Exception < StandardError
end

Một lớp exception phải được kế thừa từ lớp StandardError, trong ví dụ này chúng ta định nghĩa lớp NotOver18Exception kế thừa từ lớp StandardError.

if age < 18
    raise NotOver18Exception, "Under 18 is not allowed"
end

Để giải phóng lỗi thì chúng ta đưa thêm tên lớp vào sau từ khóa raise, rồi đến đoạn chuỗi mô tả lỗi.

Under 18 is not allowed
#<NotOver18Exception: Under 18 is not allowed>
5 1 vote
Article Rating
Subscribe
Thông báo cho tôi qua email khi
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments