Nếu thì và những cách thay thế
Contents
Bạn có biết rằng trên thế giới có một cộng đồng anti if, else. Trên đường đi tìm đường cách mệnh, coi như cũng tạo chút thử thách cho bản thân, mình đã tìm ra một số phương pháp tương đối hữu ích.
1. Thủ thuật refactor
Các phương pháp này có thể coi như là trick để code trông gọn hơn, đồng thời loại bỏ if.
Dictionary
Đây là phương pháp mình sử dụng từ lâu. Với phương pháp này thì ta có thể refactor hàm sau:
|
|
Thành dạng như sau
|
|
Enum
Nên sử dụng enum với những cái nào có chung format như ở trường hợp sau
Thay vì
|
|
Tên các method ở trên đều có chung format play**%TYPE%**. Ta có thể gọi nó thông qua *selector* để loại bỏ hẳn *if*
|
|
Để sử dụng selector, các phương thức cần gắn thêm tiền tố @objc và class cần kế thừa NSObject.
2. Sử dụng functional programming
Filter
Đây là phương thức trong bộ tam filter, map, reduce ra đời kèm ngay ở phiên bản đầu của swift. Param truyền vào filter có dạng tổng quát như sau:
(T) -> Bool // hàm truyền vào 1 giá trị và return bool
Như vậy ta có thể truyền vào tên hàm thay vì định nghĩa ra nó.
Giả sử bài toán ta đang giải quyết là tìm số nguyên dương trong mảng, thay vì viết:
|
|
Ta có thể truyền tên hàm vào trong filter để code ngắn gọn, rõ nghĩa hơn.
|
|
Nếu có nhiều hơn một điều kiện, ta có thể tổng hợp lại các hàm làm một. Giả sử ta muốn tìm số vừa lớn hơn 0 vừa nhỏ hơn 100, ta viết thêm điều kiện:
|
|
Tiếp theo, ta sử dụng phương pháp composite để có 1 hàm dạng như: lessThan100AndPositive = lessThan100 & positive. Lúc đó code sẽ gọn gàng ngăn nắp như này:
arr.filter(lessThan100AndPositive)
Nhưng trước hết, ta sẽ định nghĩa hai phương thức helper để tổng hợp điều kiện thuận tiện hơn và dễ dàng tái sử dụng lại code
|
|
Mình tạo 2 phương thức đều có tên allPass, phương thức đầu trả về Bool, còn phương thức thứ hai có dạng curry function (hàm nhận vào 1 param và trả ra 1 hàm khác). Cả 2 hàm khác nhau về cách thức tạo nhưng cùng chung 1 mục đích sử dụng (tạm thời không bàn đến bind và checkAll)
Với 2 phương thức nêu trên ta có thể composite 2 điều kiện positive và lessThan100 theo cách như sau
allPass([positive,lessThan100]) // (Int) -> Bool
Và hàm filter sẽ được viết ngắn gọn như sau:
|
|
Làm tương tự, ta có thể tạo hàm Or để tìm giá trị thỏa mãn 1 điều kiện trong tất cả các điều kiện được cung cấp.
Bạn thấy đó, với functional ta dễ dàng tách các đoạn kiểm tra điều kiện ra thành hàm riêng, dễ dàng tổng hợp chúng, đồng thời dễ test và tái sử dụng.
Để hiểu rõ hơn về hàm bind và checkAll, mình có đưa nó vào trong code ví dụ ở cuối bài viết.
Refinement type
Thế nào là refinement type
refinement type = value + predicates
Như định nghĩa trên, refinement type sẽ có 2 thành phần:
+ Giá trị
+ Điều kiện
Hai thành phần này có mối liên hệ chặt chẽ, còn liên kết thế nào thì có thể xem đoạn code ngay sau đây.
|
|
Như bạn thấy đã thấy, 100 thỏa mãn điều kiện lớn hơn 0; 99 thỏa mãn điều kiện nhỏ hơn 100 nên ta có thể sử dụng. Trong trường hợp không thỏa mãn (-1,101), giá trị nil được trả về.
Thậm chí ta chưa dùng tí if nào :D
Đoạn code định nghĩa refinement type như sau
|
|
Sử dụng sơ đồ khối để dễ hình dung
Code ví dụ source
3. Mở rộng
Ngoài những cách trên thì còn rất nhiều cách để giảm thiểu if else:
- Sử dụng đa hình
- Tách hàm
- switch case :v
- Sử dụng hàm cond (như trong Lisp)
- Blah blah
Tham khảo
-
Thư viện ramdajs https://ramdajs.com/
-
Ví dụ của [Peter tomaselli] (https://github.com/peter-tomaselli/swift-refined)
Author VietHQ
LastMod 2018-12-19