Tôi nhận được một email hỏi ý kiến về khi nào nên sử dụng đối số từ khóa (keyword arguments).
Tôi nghĩ nên mở rộng câu trả lời thành một bài viết đầy đủ về đối số phương thức Ruby để mọi người cùngได้ lợi!
Ruby rất linh hoạt khi nói đến đối số phương thức.
Chúng ta có tất cả:
Từ đối số bắt buộc tiêu chuẩn đến đối số tùy chọn & thậm chí đối số từ khóa (được đặt tên).
Trong bài viết này, bạn sẽ tìm hiểu sự khác biệt giữa tất cả các loại đối số này, và loại nào nên sử dụng tùy thuộc vào tình huống của bạn.
Sử dụng đúng loại đối số sẽ làm cho mã của bạn dễ đọc hơn & dễ làm việc hơn.
Khi băn khoăn hãy dùng Đối số Tiêu chuẩn
Đối số bắt buộc là những gì bạn nhận được theo mặc định.
Ví dụ:
def write(file, data, mode) end
write("cats.txt", "cats are cool!", "w")
Nếu bạn không truyền đúng số lượng đối số bắt buộc, bạn sẽ nhận được thông báo lỗi quen thuộc:
write("shopping_list.txt", "bacon")
ArgumentError: wrong number of arguments (2 for 3)
Điều này có nghĩa là bạn đã đưa cho phương thức 2 đối số, nhưng nó cần 3!
Phương thức Linh hoạt với Đối số Tùy chọn
Bạn có thể muốn làm cho một số đối số trở thành tùy chọn bằng cách cung cấp giá trị mặc định.
Ví dụ:
def write(file, data, mode = "w") end
Bây giờ bạn có thể gọi write với 2 đối số, trong trường hợp mode sẽ bằng giá trị mặc định ("w"), hoặc bạn có thể truyền 3 đối số để ghi đè giá trị mặc định & nhận kết quả khác.
Giá trị mặc định này giúp bạn tiết kiệm công sức khi gọi phương thức này mà vẫn cho bạn tùy chọn thay đổi nó.
Sử dụng Đối số Từ khóa để Tăng tính Rõ ràng
Một trong những nhược điểm của đối số bắt buộc & mặc định là chúng phụ thuộc vào thứ tự.
Có nghĩa là bạn phải truyền đối số theo cùng một thứ tự.
Nếu bạn truyền sai thứ tự:
Bạn sẽ nhận được kết quả không mong muốn!
Ví dụ, trong phương thức write, bạn phải gọi nó với file trước, sau đó data & cuối cùng mode.
def write(file, data, mode) end
Đối số từ khóa cho phép bạn thay đổi thứ tự các đối số.
Ví dụ:
def write(file:, data:, mode: "ascii") end
Điều này cho phép bạn gọi phương thức theo thứ tự khác:
write(data: 123, file: "test.txt")
Nhưng quan trọng hơn, khi bạn gọi phương thức bạn có thể nhìn thấy rõ ràng ý nghĩa của các đối số.
Tôi không thấy mình sử dụng đối số từ khóa thường xuyên, nhưng chúng có thể hữu ích khi bạn muốn thêm sự rõ ràng cho các lời gọi phương thức.
Giả sử chúng ta có lớp Point.
Bạn có thể viết như sau:
class Point
def initialize(x:, y:)
@x, @y = x, y
end
end
point = Point.new(x: 10, y: 20)
Cách này không có sự nhầm lẫn nào về việc số nào (10 & 20) tương ứng với giá trị x hoặc y.
Đó là một trong những mục tiêu chính khi viết mã, để tránh sự nhầm lẫn & hiểu lầm càng nhiều càng tốt.
Nếu sử dụng đối số từ khóa giúp bạn đạt được điều đó thì hãy dùng chúng.
Hãy suy nghĩ về điều này:
“Việc thêm đối số từ khóa có làm cho việc này dễ hiểu hơn không, hay nó chỉ thêm công việc & rối rắm vào mã của tôi?”
Một điều khác về đối số từ khóa là chúng rất rõ ràng về các đối số bạn đang thiếu.
Ví dụ:
point = Point.new # missing keywords: x, y
Bạn cũng có thể kết hợp đối số từ khóa với đối số thông thường.
Một chiến lược mà tôi đã quan sát được trên các phương thức built-in của Ruby là các phiên bản mới thường thêm đối số tùy chọn mới, dưới dạng đối số từ khóa.
Ví dụ: đối số từ khóa cho Float#round, Kernel#clone & String#lines được giới thiệu trong Ruby 2.4.
Sử dụng Đối số Biến đổi để Thu thập nhiều Giá trị
Điều gì xảy ra nếu bạn muốn nhận một số lượng giá trị không giới hạn?
Thì bạn có thể sử dụng đối số biến đổi (variable arguments):
def print_all(*args) end print_all(1, 2, 3, 4, 5)
Điều này cho phép bạn gọi phương thức với bất kỳ số lượng đối số nào, bao gồm cả không có đối số. Đối số biến đổi là một mảng chứa tất cả các giá trị được truyền vào.
Bạn có thể kết hợp điều này với các loại đối số khác.
Ví dụ:
def print_all(title, *chapters) end
Điều này sẽ lấy đối số đầu tiên làm title, và phần còn lại làm mảng chapters. Lưu ý rằng đối số biến đổi cần phải đặt sau tất cả đối số bắt buộc & tùy chọn, và trước bất kỳ đối số từ khóa nào.
Sử dụng Thứ tự Đúng
Dưới đây là thứ tự hợp lệ của các đối số nếu bạn muốn kết hợp chúng & tránh lỗi cú pháp:
bắt buộc -> tùy chọn -> biến đổi -> từ khóa
Dưới đây là phương thức minh họa mọi loại đối số có thể:
def testing(a, b = 1, *c, d: 1, **x)
p a,b,c,d,x
end
testing('a', 'b', 'c', 'd', 'e', d: 2, x: 1)
**x giống như đối số biến đổi, nhưng cho đối số từ khóa. Nó sẽ là một hash thay vì mảng.
Đối số “Catch-All”
Trong một số trường hợp hiếm bạn có thể thấy điều này:
def print_all(*) end
Điều này có nghĩa là phương thức chấp nhận bất kỳ đối số nào, nhưng nó không làm gì với chúng. Nó tương tự như sử dụng ký tự gạch dưới (_) bên trong block để chỉ ra đối số nào không được sử dụng.
Một ứng dụng thực tế có thể là kết hợp với từ khóa super:
class Food
def nutrition(vitamins, minerals)
puts vitamins
puts minerals
end
end
class Bacon < Food
def nutrition(*)
super
end
end
bacon = Bacon.new
bacon.nutrition("B6", "Iron")
Bây giờ khi Food thay đổi danh sách đối số của nutrition, bạn không cần thay đổi đối số trong Bacon.
Tóm tắt
Bạn đã tìm hiểu về các đối số phương thức mạnh mẽ của Ruby & cách sử dụng chúng! Đối số bắt buộc là khởi đầu tốt, sau đó bạn có thể thêm sự linh hoạt với đối số tùy chọn & thêm sự rõ ràng với đối số từ khóa.
Hy vọng bạn thấy hữu ích & thú vị!
Chia sẻ bài viết này trên Twitter để nhiều người hơn có thể xem 🙂