代码维护在委托和回调模式



首先,我只是一个初学者,目前正在用Swift语言开发一个应用程序,所以请不要太介意我的问题,因为我真的需要知道,而且我在维护我构建的代码时遇到了麻烦。

这是关于异步委托模式的。

这是我的API课程。假设有许多类似的API类进行异步调用。

protocol InitiateAPIProtocol{
     func didSuccessInitiate(results:JSON)
     func didFailInitiate(err:NSError)
}
class InitiateAPI{
    var delegate : InitiateAPIProtocol
    init(delegate: InitiateAPIProtocol){
         self.delegate=delegate
    }
    func post(wsdlURL:String,action:String,soapMessage : String){
         let request = NSMutableURLRequest(URL: NSURL(string: wsdlURL)!)
         let msgLength  = String(soapMessage.characters.count)
         let data = soapMessage.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)
         request.HTTPMethod = "POST"
         request.addValue("text/xml; charset=utf-8", forHTTPHeaderField: "Content-Type")
         request.addValue(msgLength, forHTTPHeaderField: "Content-Length")
         request.addValue(action, forHTTPHeaderField: "SOAPAction")
         request.HTTPBody = data
         let task = session.dataTaskWithRequest(request) {
              data, response, error in
            if error != nil {
                 self.delegate.didFailInitiate(error!)
                 return
            }
            let jsonData = JSON(data: data)
            self.delegate.didSuccessInitiate(jsonData)
        }
        task.resume()
}
func doInitiate(token : String){
    let soapMessage = “”
// WSDL_URL is the main wsdl url i will request.
action = “”
    post(WSDL_URL, action: action, soapMessage: soapMessage)
}
}

这是我的ViewController:

class ViewController : UIViewController,InitiateAPIProtocol{
    var initiateAPI : InitiateAPI!
    var token : String = “sWAFF1”
    override func viewWillAppear(animated: Bool) {
            // Async call start
            initiateAPI = InitiateAPI(delegate:self)
            initiateAPI.doInitiate(token)
    }
    // Here comes call back
    func didSuccessInitiate(results: JSON) {
       //handle results
    }
    func didFailInitiate(err: NSError) {
       //handle errors
    }
}

我的问题是,我说有很多类似的API类,所以如果一个视图控制器处理4个API类,那么在扩展视图控制器时,我必须处理许多协议委托方法。视图控制器下面将有许多委托方法。如果其他视图控制器调用相同的API并必须处理相同的委托,那么我在维护代码时会遇到问题,因为每次我更改一些委托参数时,我都必须修复使用这些API类的所有视图控制器上的代码。

有其他处理异步调用的好方法吗?

如果我的问题有点复杂,请留言评论,我会回复并解释清楚

委托(OOP)和"完成处理程序"(类似函数的编程)不能很好地结合在一起。

为了提高理解能力并使代码更加简洁,需要一种替代方法。其中一种方法已经由@PEEJWEEJ单独使用完成处理程序提出。

另一种方法是使用"期货或承诺"。这些极大地扩展了完成处理程序的思想,并使异步代码看起来更像同步

期货的运作方式基本如下。假设您有一个API函数,它从远程web服务获取用户。此调用是异步的。

// Given a user ID, fetch a user:
func fetchUser(id: Int) -> Future<User> {
    let promise = Promise<User>()
    // a) invoke the asynchronous operation.
    // b) when it finished, complete the promise accordingly:
    doFetchAsync(id, completion: {(user, error) in 
        if error == nil {
            promise.fulfill(user!)
        } else {
            promise.reject(error!)
        }
    })
    return.promise.future
}

首先,这里的重要事实是,没有完成处理程序。相反,异步函数会返回一个未来。future表示底层操作的最终结果。当函数fetchUser返回时,结果尚未计算,未来处于"挂起"状态。也就是说,您无法从未来立即获得结果。所以,我们必须等待??-其实不然,这将类似于带有完成处理程序的异步函数来完成,即注册一个"continuation":

为了获得结果,注册一个完成处理程序:

fetchUser(userId).map { user in
    print("User: (user)")
}.onFailure { error in
    print("Error: (error)")
}

如果出现错误,它还会处理这些错误。

函数map是注册延续的函数。它也是一个"组合子",也就是说,它返回另一个未来,您可以将其与其他函数组合,并组成更复杂的操作。

当future最终完成时,代码将继续,并在future中注册闭包。

如果您有两个相关的操作,例如OP1生成一个结果,该结果应在OP2中用作输入,并且组合的结果应返回(作为将来),您可以以全面简洁的方式实现这一点:

let imageFuture = fetchUser(userId).flatMap { user in
    return user.fetchProfileImage() 
}
imageFuture.onSuccess { image in
    // Update table view on main thread:
    ...
}

这只是对未来的一个非常简短的介绍。他们可以为你做更多的事情。

如果你想看到未来的行动,你可以在第三方图书馆FutureLib(我是作者)中启动Xcode游乐场"激励示例"。您还应该检查其他Future/Promise库,例如BrightFutures。这两个库都在Swift中实现了类似Scala的futures。

您查看过NSNotificationCenter吗?

https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSNotificationCenter_Class/

您将能够从您的api类发布事件,然后每个视图控制器都会订阅这些事件,并相应地收到通知

这有道理吗?这种模式有很多很好的例子:https://stackoverflow.com/a/24049111/2678994
https://stackoverflow.com/a/28269217/2678994

我在下面更新了你的代码:

class InitiateAPI{
    // 
    // var delegate : InitiateAPIProtocol
    // init(delegate: InitiateAPIProtocol){
    //      self.delegate=delegate
    // }
    func post(wsdlURL:String,action:String,soapMessage : String){
        let request = NSMutableURLRequest(URL: NSURL(string: wsdlURL)!)
        let msgLength  = String(soapMessage.characters.count)
        let data = soapMessage.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)
        request.HTTPMethod = "POST"
        request.addValue("text/xml; charset=utf-8", forHTTPHeaderField: "Content-Type")
        request.addValue(msgLength, forHTTPHeaderField: "Content-Length")
        request.addValue(action, forHTTPHeaderField: "SOAPAction")
        request.HTTPBody = data
        let task = session.dataTaskWithRequest(request) {
        data, response, error in
        if error != nil {
            //  self.delegate.didFailInitiate(error!)
            /* Post notification with error */
            NSNotificationCenter.defaultCenter().postNotificationName("onHttpError", object: error)
            return
        }
        let jsonData = JSON(data: data)
            // self.delegate.didSuccessInitiate(jsonData)
            /* Post notification with json body */
            NSNotificationCenter.defaultCenter().postNotificationName("onHttpSuccess", object: jsonData)
        }
        task.resume()
    }
    func doInitiate(token : String){
        let soapMessage = “”
        // WSDL_URL is the main wsdl url i will request.
        action = “”
        post(WSDL_URL, action: action, soapMessage: soapMessage)
    }
}

您的视图控制器类:

class ViewController : UIViewController { //,InitiateAPIProtocol{
    var initiateAPI : InitiateAPI!
    var token : String = “sWAFF1”
    override func viewDidLoad() {
        super.viewDidLoad()
        NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(ViewController.didSuccessInitiate(_:)), name: "onHttpSuccess", object: nil)
        NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(ViewController.didFailInitiate(_:)), name: "onHttpError", object: nil)
    }
    override func viewWillAppear(animated: Bool) {
        // Async call start
        initiateAPI = InitiateAPI(delegate:self)
        initiateAPI.doInitiate(token)
    }
    override func viewWillDisappear(animated: Bool) {
        super.viewWillDisappear(animated)
        /* Remove listeners when view controller disappears */
        NSNotificationCenter.defaultCenter().removeObserver(self, name: "onHttpSuccess", object: nil)
        NSNotificationCenter.defaultCenter().removeObserver(self, name: "onHttpError", object: nil)
    }
    // Here comes call back
    func didSuccessInitiate(notification : NSNotification) { //results: JSON) {
        if let payload = notification.object as? JSON {
            //handle results
        }
    }
    func didFailInitiate(notification : NSNotification) { //err: NSError) {
        if let payload = notification.object as? NSError {
            //handle errors
        }
    }
}

您可以(应该?)使用闭包/函数:而不是使用委托

func post(/*any other variables*/ successCompletion: (JSON) -> (), errorCompletion: (NSError) ->()){
    /* do whatever you need to*/
    /*if succeeds*/
    successCompletion("")
    /*if fails*/
    errorCompletion(error)
}
// example using closures
post({ (data) in
    /* handle Success*/
    }) { (error) in
        /* handle error */
}
// example using functions
post(handleData, errorCompletion: handleError)
func handleData(data: JSON) {
}
func handleError(error: NSError) {
}

这也将为您提供用一个函数处理所有错误的选项。

此外,在返回JSON之前,最好将其解析为所需的对象。这样可以保持ViewControllers的干净,并明确解析的发生位置。

最新更新