最近看了不少代码,想起写代码有意思的地方之一在于,实现同一个 feature,修复同一个 bug,不同程序员可以写出风格迥异的代码,甚至流程也不同,虽然最后都可行,从结果论的角度对用户来说是一致的。我们可以称这种差异为个人 taste,taste 有好坏高低之分,但有时候如何评定却很难有一个清晰准确的界定标准,一般来说代码是越简单越清晰越容易测试越好,但简单清晰容易测试又是另一个维度的标准,又会产生分歧,真要较真起来可以无穷无尽越扯越远。
我有个小例子,大家可以按自己的经验来分个优劣。
UserSession
只要涉及用户登录的 App,都少不了有个 UserSession 的类,记录和用户相关的一切数据和行为,并在用户登出的时候做销毁。UserSession 是一个一旦创建之后,大部分的业务模块都需要访问的实例对象,其他 class 如何访问 UserSession,或者说 UserSession 如何传递到各个 class,这里面的做法就多了。
问题:ControllerA,ControllerB,ControllerC 都需要访问 UserSession 实例,如何传递?
方式一:构造传递
所有的 Controller 在 init 方法里都传入 UserSession,之后再内部持有一个 strong property,类结构如下图:
如果使用这种方式,所有需要引用 UserSession 的地方都需要以 init 的方式传入,包括 Controller 内部的 View、Presenter 等对象,View 可能还有子 View,层层叠叠以树形结构,UserSession 将出现在每一个相关 Class 的 init 方法之内。
方式二:方法参数传递
Controller 本身并不持有 UserSession 的实例,每个需要用到 UserSession 的方法以参数传入,如下图:
这种方式相较方式一,UserSession 作为每个方法的参数将出现在更多的地方。显然,不持有 strong property 有他的好处,比如不会出现 Controller 无法释放导致 UserSession 也无法销毁的情况。UserSession 和 Controller 之间的依赖关系也更清楚,看 .h 中的方法就一目了然。另外需要测试某个方法的时候,要比较容易,方法的声明里就有完整的 context。
方式三:内部直接持有
Controller 在 .m 文件内部通过另一个 UserMgr 实例来统一获取 UserSession,如下图:
这种方式在 .h 中看不出 Controller 和 UserSession 的关系,在 .m 中通过另一个类(xxxMgr、xxxFactory、xxxService)来获取 UserSession 实例。好处是 .h 文件干净一些,但 .m 中可能各处都有获取 UserSession 的代码,一旦代码量多了之后很难理清 Controller 和 UserSession 二者之间的依赖关系。
以上三种方式我都见到过,不同方式对代码的影响也不同,这是个典型的例子,一个完整 App 里往往有多个类似 UserSession 需要被多处引用的对象,三种方式最后都不会影响功能的正常实现,但在代码阅读维护上存在一些差异。