-9

Tại sao bạn không nên sử dụng delegates trong swift!

Bài hôm nay tôi xin chia sẻ một bài báo về việc sử dụng delegate và calback.

Xàm xí về chút định nghĩa

Chúng ta hãy tìm hiểu tại sao chúng ta không nên sử dụng delegate trong swift liệu có chính xác hay không?

Đối với một delegate pattern thì delegate là tốt rồi. Nhưng chúng ta có con đường khác tốt hơn.

Bạn đang xây dựng một app IOS. Bạn có một mô hình mạnh mẽ. Bạn có một model, một networking layer, một UI layerlayer và cũng có thể là một số helpers.

Việc truyền dữ liệu giữa các layer trên được thông qua delegate, một pattern thực sự hữu ích và phổ biến trong IOS.

Delegation đơn giản có nghĩa là bạn uỷ quyền cho một 1 ai đó làm việc gì đó và bạn muốn người đó thông báo cho bạn những thay đổi để bạn có thể phản ứng lại với họ.

Ví dụ: Nếu một ViewController gọi đến một network service và muốn lấy thông báo khi mà services đó hoàn thành một số request nhất định, và nó sẽ thực hiện một network service's delegate. Network service sẽ gọi một số hàm delegate sau khi nó thực hiện xong.

protocol NetworkServiceDelegate {
    func didCompleteRequest(result: String)
}
class NetworkService {
    var delegate: NetworkServiceDelegate?
    
    func fetchDataFromUrl(url: String) {
        API.request(.GET, url) { result in
            delegate?.didCompleteRequest(result)
        }
    }
}
class MyViewController: UIViewController, NetworkServiceDelegate {
    
    let networkService = NetworkService()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        networkService.delegate = self
    }
    func didCompleteRequest(result: String) {
        print("I got \(result) from the server!")
    }
}

Sử dụng delegate thực sự rất tốt. Không có gì là sai với chức năng mà nó thực hiện. Nhưng chúng ta có con đường khác tốt hơn. Và tôi sẽ nói cho bạn nó tốt hơn như thế nào?

Sử dụng callbacks cho sự uỷ thác.

Callback tương tự như hàm trong delegate pattern. Chúng có điểm chung là: cho phép các đối tượng khác biết khi có điểu gì đó xảy ra và truyền dữ liệu trở lại. Vậy điều gì khác biệt ở đây? Thay vì tự tham chiếu đến chính mình thì chúng ta truyền qua một function (hàm). Hãy sử dụng function như một tài sản của bạn trong lập trình swift.

class MyClass {
  var myFunction: (String)->() = { text in
    print(text)
  }
}

MyClass có một thuộc tính myFunction cái mà có thể gọi và bất cứ ai cũng có thể thiết lập. Đây cũng là ý tưởng cơ bản của việc sử dụng callbacks thay thể delegate. Chúng ta quay về ví dụ 1 và thay việc sử dụng delegate chúng ta sẽ sử dụng callback.

class NetworkService {
    
    var onComplete: ((result: String)->())? //an optional function
    
    func fetchDataFromUrl(url: String) {
        API.request(.GET, url) { result in
            onComplete?(result: result) 
//                    ^ optional unwrapping for functions!
        }
    }
}
class MyViewController: UIViewController {
    
    let networkService = NetworkService()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        networkService.onComplete = { result in
            print("I got \(result) from the server!")
        }
    }
}

Một cách tuyệt vời để sử dụng callback là khi bạn muốn nhận dữ liệu được thông báo khi có sự thay đổi.

var onUsernamesChanged: ([String]->())?
    
var loadedUsernames = [String]() {
    didSet {
        onUsernamesChanged?(loadedUsernames)
    }
}

LƯU Ý: Bạn nên sử dụng một thuộc tính weak bên trong một closure(callback) để tránh tình trạng stop retain cycles. (bị die app).

Vậy tại sao callback lại tốt hơn?

Tách riêng.

Delegate khiến code của chúng ta bị tách ra riêng rẽ. Nó sẽ không quan trọng nếu bạn sử dụng hết protocol trong NetworkService delegate. Nhưng nếu bạn chỉ sử dụng một trong số các function của protocol của delegate thì nó lại là vấn đề. Bởi vì delegate thực hiện các protocol đó và nếu bạn sử dụng swift thay vì @obj protocol, delegate phải thực hiện mọi phương thức của protocol. Tức là bạn phải implement mọi phương thức của protocol mà bạn delegate đến nó.

Khi sử dụng callbacks, NetworkService không cần phải có đối tượng để gọi các phương thức và cũng không cần biết gì về việc ai đang triển khai các phương thức. Tất cả nó quan tâm là khi nào gọi đến phương thức đó. Ngoài ra không phải tất cả các phương thức cần được thực hiện.

Nhiều delegation

Điều gì xảy ra nêys bạn muốn thông báo cho một ViewController khi mà một request hoàn thành nhưng cũng có thể là một số class logger hay một số class analytics. Với delegates bạn sẽ có cả một mảng các delegates hoặc đến 3 thuộc tính đại diện cho delegate khác nhau.

Còn với callback: Bạn có thể định nghĩa một mảng function và gọi chúng khi công việc nào đó kết thúc. Không cần phải có một đống các đối tượng và protocol khác nhau có nguy cơ bị giữ lại một cách k cần thiết và mã code ngắn gọn hơn.

Rõ ràng trong việc tách biệt quan hệ

Sự khác nhau callback với delegate là: Delegate ở đây đóng vai trò là đối tượng gọi đến networkservice. NetworkService nói với delegate rằng: "Này, tối đã thay đổi". Còn với callbacks, Delegate đang quan sát NetworkService.

Trong thực tế, sự khác nhau là rất nhỏ.

Testing dễ dàng.

Có bao giờ bạn cảm thấy bạn test 2 lần với unit tests. Vì bạn phải mock cho tất cả protocol bao gồm tất cả delegates trong app của bạn. Với callback không chỉ bạn không phải mock cho bất kì delegates nào nhưng nó cho phép sử dụng bất cứ callback nào khi bạn muốn test.

Tài liệu dịch từ

https://medium.cobeisfresh.com/why-you-shouldn-t-use-delegates-in-swift-7ef808a7f16b


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí