从一个常见传参问题聊起

背景:

做业务的朋友一定经常会遇到参数传递的需求。之前做了很长一段时间的扩入口工作,感觉可以简单的拆解成为几个点。在新页面增加我们的点位,将上游参数传递到我们的业务域,然后根据参数展现不同的形态,用户交互后将参数流转给下游。

开发过程中经常会出现一些参数字段出问题。举个例子,我们的业务要维护多个页面,在不同页面跳转的时候,经常会遇到a页面传递给b页面,然后b页面又打开了一个新的a页面。因为一些历史原因,页面之间的链路有很多条,参数赋值的函数也有很多个。一个常见的问题是,在链路上某个节点,发生了字段的丢失/覆盖。而且历史代码中,存在大量的入口判断逻辑,导致后续不好维护。

-(void)enterWithContext:(Dictionary)dict
{
    if 入口 = a then
        self.abc = dict[abc]
        
    if 入口 = b then
        self.abc = “某个写死的值,上游不愿意传递,端上写死”
        
    if 入口 = c then
        self.abc = dict[abc] + dict[bcd] //某个需求二期,为了跟一期区分,增加了后缀,又是恶心逻辑放在端上
        
    //兜底
    if (!self.abc)
        self.abc = 兜底值 
}

这段伪代码,最初是为了某个入口设计的。要么取dictionary中的某个值,要么走兜底。但是随着业务开发,代码逐渐变臃肿复杂,代码坏味道也越来越重。后续迭代也越来越恶心。

针对这个问题。本文先介绍其中的一个办法contextService,主要解决的问题是:页面内,以及页面之间字段多次赋值。导致某个地方赋值丢失/覆盖。

问题示范:

//VC
-(void)enterWithContext:(Dictionary)dict
{
    self.abc = dict[abc]
    self.dataController.abc = dict[abc]
    self.SparkContainer.abc = dict[abc]
    self.subView.abc = dict[abc]
    
    self.bcd = dict[bcd]
    self.dataController.bcd = dict[bcd]
    self.SparkContainer.bcd = dict[bcd]
    self.subView.bcd = dict[bcd]
}

比如从VC到网络的datacontroller,到前端的sparkContainer,如果都持有一个字段,每次传递都赋值,改动的时候有多个代码改动,容易出错。

ContextService改造方案

设计

  1. 页面初始化的时候,初始化一个service,这个service会帮助VC关联自己的context
  2. 每次取字段的时候,不再通过self.abc去取。而是直接用service.xxx去取。
  3. 这个页面(VC)的dataController,sparkContainer,subView也弱持有这个service,避免字段在不同的类之间进行赋值
//ViewController
- (void)enterWithContext:(Dictionary)dict
{
    [BUILD_SERVICE(CommonParamsService)]
    [GET_SERVICE(CommonParamsService) initWithContext:dict]
}

- (DataController)dataController
{
    if (!_dataController) {
        _dataController = [DataController new]  ---> = [DataController initWithContext:self]
    }
    return _dataController;
}

//DataController
- (void)initWithContext:(id)context
{
    LinkService(context);
}

//CommonParamsService
- (void)initWithContext:(Dictionary)dict
{
     self.abc = dict[abc]
}

消费测
- (void)doSth
{
    self.abc --> GET_SERVICE(CommonParamsService).abc
}

## BUILD_SERVICE,GET_SERVICE,LINK_SERVICE这一套的实现借助了其他的一些宏。
## 本文在此做一些简化:


BUILD_SERVICE
给初始VC生成一个关联对象,这个对象包含了Dictioanry。
key是serviceName(e.g. CommonParamsService),value是Service的实例


GET_SERVICE
获取当前实例关联对象。然后根据service的name,拿到service的实例。


LINK_SERVICE
改变GET_SERVICE获取的对象。原本获取当前实例的对象,现在改为LINK_SERVICE的对象。



后续,根据业务形态,我们对service进行了划分.上面介绍的是页面级别的service,跟vc绑定。后面随着业务发展,我们还创建了跨页面级别的service。实现方法差不多:

跨页面:

  1. 创建一个NSObject的分类。姑且称之为NSObject(Context)
    这个分类,用于记录类之间的上下文信息。所以他本身有两个成员变量。一个是referContext,指向上一个context,一个是value(可以是dictionary类型),用于存储context的值。

  2. hook所有的页面push,present操作,或者统一封装一个push,present的工具组件,保证每次推出新页面的时候,新页面的referContext是老页面的context,这样,如果我们在新页面想要拿到某个值的时候,可以顺着refer链路往前寻找。

  3. 在service初始化的时候,把上一个页面的context也传进来。但是请注意,这里不需要重新初始化这个context了,避免后续页面修改了之前的页面,造成数据不一致。

  4. 找参数的时候,如果当前页面没有,那么顺着referContext往前找。直到停止条件(比如遍历到根VC了。)

跟ViewModel进行对比

可能有朋友会说,用viewModel来存放这些字段,不是差不多么。都是找一个地方,存放字段,供多个地方调用。其实还是有一些区别。
viewModel一般对应一个VC,而contextService是允许跨页面的。
viewModel一般被VC持有。而contextService是一个关联对象

收益

接入contextService后,实验组实现了GMV增长。一方面是修复了埋点丢失,找回来了一些本来就属于我们的gmv。另一方面是一些给引擎的字段被校准了,引擎根据字段判断应该使用哪个模型。使用对应模型,会让结果更加准确,持续迭代模型,长期看也是有收益的。

另一方面,开发阶段也更容易了。只需要三步:

  • service上注册新字段。
  • service初始化的时候,存入新字段。
  • 消费测取用。不需要在其他链路赋值了。
阅读全文
下载说明:
1、本站所有资源均从互联网上收集整理而来,仅供学习交流之用,因此不包含技术服务请大家谅解!
2、本站不提供任何实质性的付费和支付资源,所有需要积分下载的资源均为网站运营赞助费用或者线下劳务费用!
3、本站所有资源仅用于学习及研究使用,您必须在下载后的24小时内删除所下载资源,切勿用于商业用途,否则由此引发的法律纠纷及连带责任本站和发布者概不承担!
4、本站站内提供的所有可下载资源,本站保证未做任何负面改动(不包含修复bug和完善功能等正面优化或二次开发),但本站不保证资源的准确性、安全性和完整性,用户下载后自行斟酌,我们以交流学习为目的,并不是所有的源码都100%无错或无bug!如有链接无法下载、失效或广告,请联系客服处理!
5、本站资源除标明原创外均来自网络整理,版权归原作者或本站特约原创作者所有,如侵犯到您的合法权益,请立即告知本站,本站将及时予与删除并致以最深的歉意!
6、如果您也有好的资源或教程,您可以投稿发布,成功分享后有站币奖励和额外收入!
7、如果您喜欢该资源,请支持官方正版资源,以得到更好的正版服务!
8、请您认真阅读上述内容,注册本站用户或下载本站资源即您同意上述内容!
原文链接:https://www.shuli.cc/?p=21349,转载请注明出处。
0

评论0

显示验证码
没有账号?注册  忘记密码?