在我们日常的开发中设计模式伴随着项目的各个模块,巧妙地使用设计模式可以让我们写出更高效,简洁,优美的代码。可是因为对于设计模式的不熟悉,很多高效的设计模式并没有被很好地使用起来,而最近也正好在review同事和自己曾经写的一些代码,然后在优化的过程中谈一下我们在项目中使用设计模式做出的一些优化。当然只是个人看法,有任何的不足欢迎拍砖,大家一起探讨和成长。
1.在项目中使用delegate pattern(代理模式)和block的抉择
之前在技术交流群中看到技术大神在争论block和delegate使用哪一种该如何抉择的问题,看到有人说delegate传递的是指针而block传递的是一个结构体,在这个角度出发确实是delegate更好一些,但是在我们实际的开发环境中,我更愿意结合具体的业务场景做出不一样的选择,因为delegate在代码的可读性上是不如block好的,具体而言我们主要做了如下的抉择。
首先在网络请求层的处理上对delegate和block都做了封装,原因就是在具体的业务中会出现在A类中发送了请求,可是我们返回的数据创建的对应模型要在B类中才会去调用,这个时候我们使用block来传递数据模型,因为这时使用代理可能要封装两层甚至多层的代理这里无疑就将代码的可读性变得很差。如果模型传递的更多层,代码会显得异常混乱。
第二在实际开发中有些复杂界面会用到dispatch_group_t的情况,这个时候如果是要对网络请求做合并处理的,如果这个时候在同一个方法中我们发送了两个甚至更多的网络请求,此时如果要是使用delegate来回调返回结果,一般会将dispatch_group_t声明成一个属性,在代理中使用dispatch_group_leave(self.group)这样的方式来获取回调结果,然后在dispatch_group_notify中做统一的处理,我认为这样对于group的内存管理就变得不在合理,所以这个时候我是用的block。 剩下的在一个界面中的时候,我更多的会用#prama mark 的方法区分出一个请求,用代理的方式会写请求。
当然除了网络请求层的处理之外,我们是不是在其他地方也对delegate和block有过抉择呢,这一点上一定会有的。我记得前不久我们在做 <U运动>健身教练直播版本开发的过程中就遇到过,我相信大家对于直播界面是不陌生的,如果点击主播头像,观众头像,评论区的昵称都会显示用户的资料弹窗的。可是这三个部分被拆分到了三个小的试图类中,当时同事提出每个点击事件用block的方式进行回调,写一个manager管理类,对弹出用户资料进行统一的处理,我想很多人第一时间都会想到这样的处理方案,但是后来却用了代理的方案。
方案的代码结构如上图,就是一个简单的demo,也就是说我们将代理模式进行了区分,所有获取用户资料的点击事件都走baseMethod1的代理 一样可以做到所有用户的获取资料的点击事件走到controller的一个方法中,也不再需要去为此创建一个manager,甚至也不需要在每个试图中区公开一个回调的方法,感觉在代码的简洁和清晰上都有了提高,此外也可以兼容每个试图有自己的回调事件。所以在delegate模式和block的抉择中并不是一定的,要结合具体的业务的场景做出自己的分析然后做出选择。
2.关于adapter pattern(适配器模式)的使用
我想一些同学在陪伴项目成长的过程中很多都遇到过这样的一个问题,就是将服务器返回的数据转换成一个数据模型然后传入各个试图的接口渲染界面,但是有的时候产品告诉我们,我的用户资料想多要一个参数来呈现,如果接口名命名成多个参数名的形式,那么势必会很麻烦改动很多曾经调用的地方。这个时候有的小伙伴会说接口传入参数是一个类就好了,但是如果用户资料要在多个界面进行呈现的方式是一样的,但是每个界面整体返回的数据又是不一样的情况下,如果接口用类来做参数,可能就需要在拆分出来的用户资料的试图中提供很多的接口,然后暴露给界面调用,显然这样的方式也是不合理的。所以这个时候怎么样解决呢,于是出现了适配器模式。代码结构如下
如果在适配器模式下,我们再给多处需要用到的试图传入参数的时候就不用接口定义多个参数或者是类了,这个时候我们定义的参数是遵守某个协议的数据模型,我们在每个界面返回的数据模型中遵守对应的协议就可以了。如果产品再来告诉我们改需求,我们就在协议中对应的添加方法,然后搜索下遵守对应协议的类实现这个方法就好了,界面的处理就会变得简单。当然对于适配器的使用我只是简单的分析。它绝不只是用在适配界面,我们在真实的项目中进入有些controller也是用的这样的方案,甚至适配器协议还要再去做遵守协议的操作,在它的上层在创建一个适配器,这个就需要结合具体的业务场景了。但是适配器在增加代码的可读性以及简化代码上真的可以给我们很大的帮助,更好的使用还是需要小伙伴们不断地尝试和想象的~~~
3.关于singleton pattern(单例模式)的使用
我想对于绝大多数开发者而言,这种开发模式对于我们而言都是再熟悉不过的。这里要说单例模式的原因是我认为单例在很多的项目中可能有些被过分的使用了,一个项目中可能会出现几十个甚至更多的单例。
我们都知道,在一个单例被创建之后会伴随客户端的整个生命周期,在程序结束之前单例是不会被释放的,所以从内存管理的角度上单例的使用是应该被注意的。应该知道什么场景下我们适合使用单例,在什么样的业务场景下我们可以避免使用单例以节省我们的内存空间。比如说加入我们做了一个直播,有些同学可能为了记录用户是不是在直播中去创建一个单例来判断用户当前的状态,然后限制用户的一些行为。其实这样的场景下,我认为使用单例就是一种不合理的行为,因为在这种模式下,我们完全可以在直播管理类中创建一个类方法创建一个方法,将当前状态存储在NSUserDefault中进行状态的判断,这样节省了对应的内存,也避免了不合理的浪费内存空间,淡然和这里相似的距离有很多。希望处理不当的同学可以检查一下自己的项目,做一个更优的处理方案。
4.一些其他的设计模式中可能需要注意的问题
除了上边的最常用的设计模式之外,我们在项目中常用的设计模式还有工厂模式,观察者,策略等模式和部分项目中用到的中介者,模板,享元等模式。
首先想说的就是观察者模式,在iOS实际开发中,我想大家都用到过NSNotificationCenter和KVO或者FBKVO.但是我想不是所有同学都知道NSNotificationCenter 的observer 收到消息的方法中所在的确切线程的。其实收到消息后执行的方法所在的线程是和post消息时所在线程是一致的,所以这是可能就会出现线程安全的问题,虽然苹果在官方的文档中说做过处理,但是在实际开发中并不意味着不会出现问题。具体可以参考这篇文章,里边介绍了出现问题的场景,在我们的项目中使用KVO时遇到过这样的情况的。
至于策略模式,我想大家日常的开发中都有用到过,只是我们有些同学不知道这个叫策略而已~~~其实策略的使用使用真的可以大幅度的提升程序的可读性的,所以不知道的小伙伴一定要百度一下,我在这里就不详细介绍啦。
而中介者模式个人感觉主要用在组件化开发中做URL的跳转使用,我们的项目一直希望想向组件化开发的方向做努力,所以最近可能会深入的研究这一部分,如果有了成果在和童鞋们分享,要说的太多,这里就不做扩展了。
模板模式在项目中的使用其实是可以和适配器模式相结合一起来玩的,所以研究过适配器的小伙伴,对于这里一定再熟悉不过,如果对于这块不是很了解,真的建议很好地去进行学习和关注,良好的代码质量很重要。
最后说道享元模式,我说很少的项目中用到其实是个很严重的错误,因为每个项目我相信一定都会有tableview而它的cell复用池机制就是基于享元模式设计的,严格上说享元模式在企业项目中被自己设计和使用还是比较少的。但是当我们需要复用大量数据的时候考虑到性能等方面的问题,不防考虑下这个模式,然后做相应的优化,一定会有意想不到奇效~~~
文章的最后,想说其实我这个人很懒,不喜欢写文章,自己写的一些小实验也都傻傻的停留在了自己的电脑上,但是最近身边发生的一些事让我的态度有了改变,所以从今以后,我应该会强制的要求自己每周至少写一篇满满都是自己浅显看法的文章,将自己的小想法和无聊生产的小轮子都放到git上去,有任何意见和不满欢迎在这里吐槽,拍砖。当然更希望可以和热爱iOS开发的童鞋们一起成长~~~