iOS重构之面向协议编程实践

作者: 文兴 | 来源:发表于2017-10-15 22:01 被阅读857次

    最近一段时间都在进行iOS客户端的重构,参考了许多iOS重构方面的资料,在重构的过程中也遇到一些困难,同时总结了不少经验,在这里和大家分享一下。这将会是一个系列的文章,每一篇文章都会从一个具体的、普遍性的问题出发,然后分析和解决这个问题。

    插一句,软件开发本身是一件工程化的事情,虽然有一些理论上的指导和支持,但终归还是要从实际项目中出发,找到适合项目本身的最优解。我在这里提到的一些实践不一定适合每一个项目,甚至在某些情况下不适用,所以欢迎大家多多讨论交流,我们的终极目标都是写出具有可读性、可维护性、可拓展性的高质量代码。

    问题:如何管理界面跳转的代码

    回想一下你的项目中,大部分界面跳转的代码是写在哪里的?最简单方便的,直接写在ViewController中。这种方式很好理解,因为ViewController原生就提供了界面跳转的方法prsentVC/pushToVC。
    随着项目逐渐复杂,你可能会发现,有一些VC可以从很多不同的VC跳转而来,如果按照原来的方式,就会有很多界面跳转代码的重复。于是我们可能会创建一个XXRouter的类,把跳转到XXViewController的代码提取出来,放到XXRouter中管理。

    class XXRouter {
      var newVC:XXViewController {
        // 或者从Storyboard/Xib创建
        return XXViewController()
      }
      func presentXXVC(_ fromVC:UIViewController){
        let xxVC = newVC
        //XXViewController初始化
        vc.present(xxVC)
      }
    }
    

    于是,在其他需要跳转到XXViewController的VC中,只需要新建一个XXRouter,调用router.presentXXVC()就可以实现界面跳转,也做到了统一管理。

    class MyViewController:UIViewController {
      fileprivate var router = XXRouter()
      func someFunc() {
        // do something 
        router.presentXXVC()
      }
    }
    

    这种方式已经能满足我们大部分的要求了,只不过每次都需要创建一个Router实例,当然也可以使用单例。
    其实我们可以借助Swift的协议扩展Protocol Extension,可以把Router的实现变得更加直观和优雅

    协议扩展Router模式

    Swift相比其他OOP语言的一个比较大的特点就是面向协议Protocol Oriented,理论知识这里就不在赘述了,给一个苹果的官方文档的链接Protocol-Oriented Programming in Swift
    下面就是我们的实践,这次我们使用protocol来处理我们的界面跳转的代码

    protocol XXRouter {}
    extension XXRouter {
      var newVC:XXViewController {
        // 或者从Storyboard/Xib创建
        return XXViewController()
      }
      func presentXXVC(_ fromVC:UIViewController){
        let xxVC = newVC
        //XXViewController初始化
        vc.present(xxVC)
      }
    }
    

    看起来和之前的实现几乎一模一样,但是在VC中我们就不需要创建XXRouter的实例,而是直接让ViewController实现我们的XXRouter

    extension MyViewController:XXRouter{}
    class MyViewController:UIViewController {
      func someFunc() {
        // do something 
        self.presentXXVC(self)
      }
    }
    

    这种方式还有一个好处就是可以很直观的看到界面间的跳转关系,尤其是一个界面可以跳转到不同界面的情况,比如

    extension MyViewController:XXRouter,YYRouter,ZZRouter{}
    class MyViewController:UIViewController{}
    

    上面的代码几乎达到了代码即文档MyViewController这个界面,可以跳转到XX、YY和ZZ三个ViewController。
    更进一步,借助extensionwhere条件扩展,我们还可以省略掉fromVC呢!

    protocol XXRouter {}
    extension XXRouter where Self:UIViewController {
      var newVC:XXViewController {
        // 或者从Storyboard/Xib创建
        return XXViewController()
      }
      
      func presentXXVC(){
        let xxVC = newVC
        // XXViewController初始化
        // 直接使用self,因为where指定当前扩展的是UIViewController
        self.present(xxVC)
      }
    }
    

    这种方式修改的成本很小,只是提取和挪动界面间跳转代码,几乎不会出现什么错误,用非常小的成本很大程度提高了项目代码的可读性和可维护性。目前我们项目中的界面跳转就是用这种方式进行了重构。

    其他的跳转方案

    最后,再简单分析一下其他两种界面跳转的解决方案

    1. Storyboard的segue跳转
      这种方式对于Storyboard内ViewController之间的简单跳转来说十分方便,不用写一行代码。而且.storyboard文件中把一个个分离的ViewController通过图的方式有机地连接在一起,清晰得展现出了项目内ViewController跳转的路径。
      不足:
      涉及到页面之间传值或者需要做额外初始化工作的ViewController,需要在代码中实现delegate,和直接代码跳转相比没有太大差别。
      对于需要额外初始化的ViewController,把一个跳转流程分离到两个地方实现,个人觉得更加不利于维护。
    2. 模仿前端的URL Router跳转,比如MGJRouter
      这种方式受Web URL跳转方式的启发,通过注册页面为URL Scheme的方式进行跳转,比较适合Hybird应用,给跳转网页和跳转ViewController一个统一的入口,便于维护
      不足:
      额外多了注册ViewController或实现Router Protocol的操作。
      侵入性大,需要在项目初期就使用这种方式,不利于重构。

    这篇文章就写到这里啦,iOS重构的文章还有挖了几个坑,近期会填完!如果各位觉得本文对你有帮助的话,请点一个喜欢,谢谢~

    相关文章

      网友评论

        本文标题:iOS重构之面向协议编程实践

        本文链接:https://www.haomeiwen.com/subject/caxmlxtx.html