Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> Ruby

Tránh Case Statement trong Ruby: Hưởng thụ Đa hình cho Code Sạch hơn

Bạn có đang dùng toàn bộ sức mạnh của OOP (Lập trình Hướng đối tượng) hay đang bỏ lỡ?

Nếu bạn ra quyết định dựa trên kiểu của đối tượng thì bạn đang bỏ lỡ một tính năng OOP quan trọng: đa hình.

Quyết định kiểu thường được làm bên trong câu lệnh case (không thân thiện với OOP) và trong bài này bạn sẽ học cách viết code tốt hơn bằng cách loại bỏ chúng.

Kiểm tra Kiểu

Để tôi bắt đầu bằng ví dụ nơi chúng ta không tận dụng đa hình.

Chúng ta muốn triển khai trò chơi "Kéo, Búa, Bao" và quyết định có một lớp Game và một lớp cho mỗi nước đi có thể.

Để kiểm tra người thắng, chúng ta sẽ triển khai phương thức play trên Game:

class Game def self.play(move1, move2) return :tie if move1 == move2 move1.wins_against?(move2) end end

Và đây là một trong các nước đi (các nước khác theo cùng mẫu):

class Rock def wins_against?(other_move) case other_move when Paper then false when Scissors then true end end end

Bây giờ chúng ta có thể gọi phương thức play với hai nước đi và biết nếu nước đầu tiên thắng.

p Game.play(Rock.new, Paper.new) # false

Ổn, cái này hoạt động, nhưng chúng ta có thể tốt hơn không? Chúng ta có thể loại bỏ câu lệnh case xấu xí đó không?

Đa hình Thay vì Kiểm tra Kiểu

Có! Chúng ta có thể loại bỏ câu lệnh case kiểm tra kiểu bằng cách dùng nguyên tắc OOP cơ bản.

Ý tưởng là dùng thực tế chúng ta biết lớp hiện tại để hỏi đối tượng nước đi khác xem nó có đánh bại chúng ta không.

Và chúng ta sẽ dùng tên phương thức cụ thể cho lớp của chúng ta (cho Rock tên phương thức có thể là: do_you_beat_rock?)

Trong Ruby, đa hình là khả năng gửi bất kỳ lời gọi phương thức nào (còn gọi là thông điệp, trong thuật ngữ OOP) đến bất kỳ đối tượng nào mà không cần kiểm tra lớp của đối tượng. Điều này còn gọi là "duck typing", nhưng tôi không thích thuật ngữ đó 🙂

Trong Java (chịu với tôi một chút...), bạn có cái gọi là "interfaces" cho phép bạn bắt buộc tập phương thức được triển khai bởi lớp ở cấp trình biên dịch.

Chúng ta không có đó trong Ruby (có lẽ tốt hơn), vì vậy bạn phải dựa vào test.

Hãy xem ví dụ mã triển khai mới này trông như thế nào:

class Rock def wins_against?(other_move) other_move.do_you_beat_rock? end def do_you_beat_paper? false end def do_you_beat_scissors? true end end

Lưu ý câu lệnh case đã biến mất. Nó đã được thay thế bằng một lời gọi phương thức đơn và hai định nghĩa phương thức.

Cập nhật: Như một số độc giả đã bình luận, tôi không nhận ra logic bị đảo ngược khi triển khai mẫu này. Giải pháp đề xuất bởi Daniel P. Clark là chỉ đảo thứ tự trong phương thức play thành move2.wins_against?(move1).

Cái này sạch hơn nhiều phải không? Bạn nghĩ sao?

Thêm Một Nước Đi!

Bây giờ giả sử bạn muốn thêm nước đi mới. Bạn phải thay đổi gì?

Fikir một chút...

Với cách tiếp cận case statement, bạn phải thêm nhánh mới cho mọi nước đi. Lưu ý bạn bị "buộc" thay đổi phương thức hoạt động hoàn hảo để giới thiệu chức năng mới.

Nhưng đó không phải vấn đề với phiên bản OOP hơn của chúng ta. Tất cả chúng ta phải làm là thêm phương thức mới. Đây là nguyên tắc mở/đóng (chữ "O" trong SOLID) đang hoạt động.

Tùy chọn khác có thể dùng metaprogramming (với method_missing hoặc define_method).

Tôi có thực sự dùng metaprogramming cho cái này không?

Có lẽ không, trừ khi số nước đi thay đổi liên tục, hoặc có số lượng lớn nước đi.

Có chi phí cho metaprogramming, nó không phải là đạn bạc như một số người tin. Bạn sẽ đổi hiệu suất và khả năng đọc cho chút linh hoạt thêm.

Kết luận

Trong bài này bạn học rằng bạn nên tránh dùng câu lệnh case để kiểm tra kiểu lớp. Thay vào đó, bạn nên tận dụng đa hình.

Điều này sẽ giúp bạn tạo code tốt hơn có thể mở rộng bằng cách thêm mã mới thay vì thay đổi mã hiện tại. Bây giờ đến lượt bạn hành động và làm một chút refactoring 🙂

Btw điều này không có nghĩa là tôi ủng hộ loại bỏ hoàn toàn case statements khỏi hộp công cụ của bạn, nhưng bạn nên cảnh giác bất cứ khi nào bạn thấy mình viết cái. Hãy chắc chắn đó là giải pháp tốt nhất cho vấn đề.

Đừng quên chia sẻ bài viết này nếu bạn muốn tôi tiếp tục viết các bài như thế này!