Tạm biệt OOP, chào đón POP
1. Tại sao nên từ bỏ OOP: OOP thật sự sự rất tuyệt vời, trái tim của Cocoa chính là OOP, không có OOP chúng ta không thể viết được 1 ứng dụng iOS nào. Nhưng với OOP vẫn có những điều chưa thật sự tốt. 1.1 Những hạn chế của OOP: Khi bạn tạo 1 subclass từ 1 class bạn phải kế thừa cả thuộc tính ...
1. Tại sao nên từ bỏ OOP:
OOP thật sự sự rất tuyệt vời, trái tim của Cocoa chính là OOP, không có OOP chúng ta không thể viết được 1 ứng dụng iOS nào. Nhưng với OOP vẫn có những điều chưa thật sự tốt.
1.1 Những hạn chế của OOP:
- Khi bạn tạo 1 subclass từ 1 class bạn phải kế thừa cả thuộc tính lẫn các phương thức từ lớp cha mà có khi bạn không thật sự cần đến chúng, làm cho subclass của bạn bị thừa các phương thức hoặc thuộc tính không cần thiết.
- Tại subclass quá nhiều làm bạn khó control việc sử dụng các class cũng như là fix bug hoặc edit.
- Chúng ta thường tạo các subclass chỉ để lấy được hàm mong muốn mà quên gom các class liên quan lại, không cần phải dùng đa kế thừa thêm phức tạp, ví dụ: kết hợp 2 subclass của 1 UITextField lại để tạo ra 1 super UITextField.
1.2 1 số vấn đề OOP trong thực tế:
1.2.1 Login App:
Giả sử chúng ta đang làm 1 màn hình login cho app iOS
khi ấn vào nút login nếu password hoặc username bị sai thì nút login và error message sẽ rung lên báo hiệu cho người dùng biết. Theo cách truyền thống từ trước đến nay chúng ta sẽ tạo ra 2 class ShakingButton và ShakingLabel với cùng 1 function doShaking
class ShakingButton: UIButton { func doShaking() { } } class ShakingLabel: UILabel { func doShaking() { } } @IBAction func loginPressed(sender: AnyObject) { if loginError() { errorMessageLabel.doShaking() loginButton.doShaking() } }
ở 1 viewController khác chúng ta lại muốn tạo 1 button không thực hiện shake mà thực hiện buzz
class BuzzingButton: UIButton { func doBuzzing() { } }
h ta lại muốn kết hợp cả hành động lại làm 1, sẽ có lớp ShakingBuzzingButton, sau đó lại bla bla các hành động khác mà vẫn rõ nhìn rõ từng loại button khi nhìn vào tên ta phải tạo ra ShakingBuzzingBlaBlaButton
1.2.2 SubClass UIViewController
chúng ta tạo 1 lớp để show error trong viewController
class PresentErrorViewController: UIViewController { func presentError() { } }
các class sau muốn dùng lại hàm presentError thì đều phải kế thừa từ class PresentErrorViewController, 100 class, 1000 class ...
2. Chào đón POP:
POP là thuật ngữ viết tắt của Protocol Oriented Programming, bản chất của protocol giống như 1 huấn luyện viên bóng rổ anh ta chỉ cho các cầu thủ trong đội chơi như thế nào nhưng bản thân anh ta không biết làm thế nào để chơi bóng rổ.
2.1 Tại sao lại là POP:
Quay trở lại với vấn đề đầu tiên, trong swift ngoài subClass chúng ta vẫn còn nhiều cách khác để giải quyết.
2.1.1 Sử dụng extension:
Thay vì phải tạo subClass UIButton ta chỉ cần extension UIView, bởi vì UIButton kế thừa từ UIView nên chúng đều có thể gọi đc các phương thức extension từ UIView
extension UIView { func doShaking() { } func doBuzzing() { } }
Tuy nhiên vì UILabel Cũng kế thừa từ UIView nên vô hình chung nó cũng có thể gọi được doBuzzing(), việc đó tạo ra những chức năng kế thừa không cần thiết có thể làm Object bị phình to ra.
2.1.2 POP con đường mới:
Không cần subClass chúng ta chỉ cần tạo các Protocol
protocol ShakingProtocol { } extension ShakingProtocol where Self: UIView { func doShaking() { } } protocol BuzzingProtocol { } extension BuzzingProtocol where Self: UIView { func doBuzing() { } }
tiếp đó ta tạo các button, label adopt các protocol tương ứng
class CustomButton: UIButton, ShakingProtocol, BuzzingProtocol { } class CustomLabel: UILabel, ShakingProtocol { }
2 đối tượng CustomButton và CustomLabel đều cùng adopt protocol ShakingProtocol nên chúng đồng thời gọi được hàm doShaking, riêng CustomButton adopt thêm protocol BuzzingProtocol nên nó có thể gọi thêm hàm doBuzzing
loginButton.doShaking() loginButton.doBuzing() errorMessageLabel.doShaking() //errorMessageLabel.doBuzing() -> error
Điều tuyệt vời của POP ở đây là chúng ta có thể thực thi được hàm doShaking hoặc doBuzzing với bất kỳ đối tượng nào mà không cần subClass tất cả, chỉ đơn giản là adopt protocol tương ứng. Điều đó cũng giúp cho việc đặt tên các đối tượng của chúng ta dễ dàng hơn, không cần dài dòng ý nghĩa của nó đã được thể hiện thông qua tên protocol nó adopt.
Tiếp đến với ví dụ 2 thay vì phải tạo 1 ViewController PresentErrorViewController rồi để các viewController khác kế thừa từ nó ta chỉ cần tạo 1 protocol PresentErrorProtocol, sau đó extension UIViewController adopt PresentErrorProtocol.
protocol PresentErrorProtocol { func presentError() } extension UIViewController: PresentErrorProtocol { func presentError() { } }
Tất cả ViewController mà kế thừa từ UIViewController đều có thể gọi presentError mà không cần kế thừa từ bất kỳ ViewController nào.
3. Tham khảo :
- https://www.raywenderlich.com/148448/introducing-protocol-oriented-programming
- https://developer.apple.com/videos/play/wwdc2015/408/
4. Link Demo :
https://github.com/pqhuy87it/MonthlyReport/tree/master/Protocol