通过Python脚本支持OC代码重构实践(三):数据项使用模块接入数据通路的适配

news2025/1/21 12:11:49

作者 | 刘俊启

导读

在软件开发中,经常会遇到一些代码问题,例如逻辑结构复杂、依赖关系混乱、代码冗余、不易读懂的命名等。这些问题可能导致代码的可维护性下降,增加维护成本,同时也会影响到开发效率。这时通常通过重构的方式对已有代码结构进行改进和优化。在重构的工作中,大部分的工作是人工的方式完成,是一个耗时且容易出错的过程。对于研发人员来讲,在不改变软件的功能和行为的前提下,保证质量和效率完成对已有功能的重构,是一个极大的挑战。本系列以Python实现自动化的工具,支持代码重构过程的实践。

在第一篇《通过Python脚本支持OC代码重构实践(一):模块调用关系分析》的内容中,介绍了使用Python实现模块调用关系的分析,确定了调用数据项的代码块超过了600处,如图-1所示,这些调用点分布在不同的组件中,是直接调用的关系。

图片

△图-1

在第二篇《通过Python脚本支持OC代码重构实践(二):数据项提供模块接入数据通路的代码生成》的内容中,重点介绍了使用Python实现了数据项提供模块接入数据通路时,公开数据项相关的代码生成(图-2中的红框部分),这时数据项读写由原来的直接读写方式改为通过数据通路的间接读写方式。

图片

△图-2

数据项提供模块接入到数据通路后数据项使用模块需要进行重构,以符合数据通路的标准。重构涉及到600多处调用代码段的适配(图-3中的红框部分),手工重构方式成本高、出错概率高,并且在测试时需要逐项验证,成本也很高。为了解决这个问题,我们使用Python脚本实现了与数据通路的通讯代码的生成,可自动的为每个数据项封装读写函数,和自动将原有的代码调用替换为升级后的代码调用,支持不同数据项的升级。这样做实现了本次重构工作在测试及上线阶段零 Bug。

图片

△图-3

本篇内容阐述如何利用Python编写的自动化工具,实现将原数据项使用模块中直接对数据项提供模块中数据项的读写方式,升级为通过数据通路间接读写。包括每个数据项读写类的封装数据项使用模块的调用代码段适配

01 数据项读写类封装

为了降低数据项的读写调用代码的重构成本,在数据项使用模块中创建一个封装类。每个数据项的读写创建一个静态函数来实现,可被数据项使用模块中的数据项读写类使用。由于需要使用Python脚本实现工具,因此需要有明确的生成规则,以便工具的实现。规则如下:

1、数据项的读取操作,函数返回类型,函数名,均与与数据项相同。

  • 如:NSString*value1; 需要转为 +(NSString *)value1,包含函数定义及实现。

2、数据项的更新操作,set_ + 函数名:数据类型,均与数据项相同。

  • 如:NSStringvalue1; 需要转为 +(void)set_value1:(NSString)value,包含函数定义及实现。注意:参数名均为value

1.1 数据项读取能力封装

基于数据项读取操作生成规则,分别实现函数头、函数声明、函数体,分别输出至.m和.h文件。

  • 函数头及函数声明实现示例
# 原代码行示例 NSString value1; 
matchObj = re.match(r"(.*)\s+(.*);", line, re.M|re.I)
if matchObj:
    valuetype = matchObj.group(1)
    valuename = matchObj.group(2) 
    # 因不同类型修饰的方式不同,在getReadFunReturnType进行类型映射;如NSString :NSString *
    funname = '+ (' + getReadFunReturnType(valuetype) + ')' + valuename
    # 函数声明 .h文件 + (NSString *)value1;\n\n
    hfunname = funname + ';\n\n'
    # 函数定义 .m文件 + (NSString *)value1 {\n
    mfunname = funname + ' {\n'
  • 函数体示例,每个数据项跟据key与数据通路通信,读取数据项
    # 定义返回类型的变量,并赋值,代码行为
    # funbody为:    NSString *res = [DataChannelReaderxxx 
    funbody = '    ' + getReadFunReturnValueType(valuetype) + 'res = [DataChannelReaderxxx '
    # 不同类型的数据,数据通路提供的读取的函数不同,由getReadFunName函数中映射,如:NSString :stringForKey
    # funbody 为 NSString *res = [DataChannelReaderxxx stringForKey:@"
    funbody += getReadFunName(valuetype) + ':@\"'
    # key,类名_数据项名 className_value1
    key = className + '_' + valuename
    # funbody 为 NSString *res = [DataChannelReaderxxx stringForKey:@"className_value1"];\n
    funbody += key + '\"];\n'
    # 函数实现完成
    funbody += '    return res;\n}\n\n'
  • 分别存到.m文件和.h文件
    # 函数数头 .m文件
    file_data += mfunname
    # 函数体 .m文件
    file_data += funbody
    # 函数定义 .h文件
    hfile_data += hfunname
  • 文件生成:默认以XXXSettingReader作为文件名及类名作为前辍,XXX为使用方模块名称,这样就比较清楚,是那个模块中的数据项读取能力封装。

1.2 数据项更新能力封装

基于数据项更新操作生成规则,分别实现函数头、函数体,及.m和.h文件

  • 函数头及函数声明实现示例
# 原代码行示例 NSString value1; 
matchObj = re.match(r"(.*)\s+(.*);", line, re.M|re.I)
if matchObj:
    valuename = matchObj.group(2) 
    valuetype = matchObj.group(1) 
    # funname为: + (void)set_value1
    funname = '+ (void)set_' + valuename
    # 因不同类型修饰的方式不同,在getValueType进行类型映射;如NSString :NSString *
    # funname为:+ (void)set_value1:(NSString *)value
    funname += ':(' + getValueType(valuetype) + ')value'
    # 函数声明 .h文件 + (void)set_value1:(NSString *)value;\n\n
    hfunname = funname + ';\n\n'
    # 函数定义 .m文件 + (void)set_value1:(NSString *)value {\n
    mfunname = funname + ' {\n'
  • 函数体示例,每个数据项跟据key与数据通路通信,更新数据项
    # 不同类型的数据,数据通路提供的更新的函数不同,在getUpdateFunName函数中映射,如:NSString :updateString
    # funbody 为 [DataChannelWriterxxx updateString:value 
    funbody = '    [DataChannelWriterxxx ' + getUpdateFunName(valuetype) + ':value ' 
    # key,类名_数据项名 className_value1
    key = className + '_' + valuename
    # funbody 为 [DataChannelWriterxxx updateString:value forKey:@"className_value1"];\n
    funbody += 'forKey:@\"' + key + '\"];\n'
    # 函数实现完成
    funbody += '    }\n\n'
  • 分别存到.m文件和.h文件
    # 函数数头 .m文件
    file_data += mfunname
    # 函数体 .m文件
    file_data += funbody
    # 函数定义 .h文件
    hfile_data += hfunname
  • 文件生成:默认以XXXSettingWriter作为文件名及类名作为前辍,XXX为使用方模块名称,这样就比较清楚,是那个模块中的数据项更新能力封装。

02 数据项使用模块调用代码段适配

数据提供模块通过数据通路支持数据项的读写,在数据项使用模块中也需要进行适配。原直接使用数据项,改为使用数据项读写类,这部分的代码使用自动化方式完成。分为两类,数据项更新调用代码段适配数据项读取调用代码段适配,因数据项更新和数据项读取代码段前辍相似,先执行更新后执行读取。

2.1 数据项更新调用代码段适配

2.1.1 代码转换OC代码示例

数据项读取的更改的主要思路为字符串匹配,查找替换。依次的拼装每个数据项字串,再替换成每个数据项升级之后的写法,如:

[XXXSetting share].value1 = @"str"  => [XXXSettingWriter set_value1:@"str"]

2.1.2 关键的代码实现

  • 原始数据项调用字串使用数据通路的数据项绑定
# 定义个全局的字典
allwritepubvalue = {}
# 原代码行示例 NSString value1; 
matchObj = re.match(r"(.*)\s+(.*);", line, re.M|re.I)
if matchObj:
    # valuename = value1
    valuename = matchObj.group(2) 
    # key = [XXXSetting share].value1
    key = '[XXXSetting share].' + valuename
    # value = SettingWriter set_value1:,不同的模块前面加上[XXX ,后面加运算符右侧
    value = ' XXXSettingWriter set_' + valuename + ':'
    # 赋值 key = [XXXSetting share].value1,value = SettingWriter set_value1: 
    allwritepubvalue[key] = value
  • 查找原调用方式,升级为数据通路的读取方式
# 获取当前工程中,所有源码文件及对应的组件名
allfileandlib = {}
# allpubvalue 全局变量,字典
for key, value in allwritepubvalue.items():
    # filename为文件名,libname为组件名
    for filename, libname in allfileandlib.items():
        # 当libname为XXX replacevalue = [XXXSettingWriter set_value1:
        replacevalue = '[' + libname + value
        # 实现个函数 重写这个文件 将文件中 [XXXSetting share].value1 = YYY 替换为 [XXXSettingReader set_value1:YYY]
        reWriteFile(filename, key, replacevalue)   
  • 文件重写函数实现,需要实现全字的匹配,避免数据中存在相互为子串的情况。
# 定义一个输出的数据,初始为空字串
outfiledata = ''
# 使用正则全字匹配,查找替换
regAbKey = fromstr.replace('[', '\[')
regAbKey = regAbKey.replace(']', '\]')
regAbKey = regAbKey.replace('.', '\.')
# pattern 为 .*\[XXXSetting share\]\.value1\s*=\s*([a-zA-Z0-9_\[\]\s\.]+),为了匹配赋值字串,但没有考虑运算符右侧有运算符的情况
pattern = r'.*' + regAbKey + '\s*=\s*([a-zA-Z0-9_\[\]\s\.]+)'
# 依次从文件中读,正则全字查找及规换
for line in f:
    matchObj = re.match(pattern, line, re.M|re.I)
    if matchObj:
        # 代码中真实的写法,去掉前面的一些代码,比如    [XXXSetting share].value1 = YYY,变为[XXXSetting share].value1 = YYY
        eqcode = re.sub(r'.*' + regAbKey, fromstr, matchObj.group())
        # 如原代码为 [XXXSetting share].value1 = YYY ,则 matchObj.group(1) 为YYY
        # 把 [XXXSetting share].value1 = YYY 替换为 [XXXSettingWriter set_value1:YYY]
        newline = line.replace(eqcode, tosrt + matchObj.group(1) +']')
        outfiledata += newline

2.2 数据项读取调用代码段适配

2.2.1 代码转换OC代码示例

数据项读取的更改的主要思路为字符串匹配,查找替换。依次的拼装每个数据项字串,再替换成每个数据项升级之后的写法,如:

[XXXSetting share].value1 => [XXXSettingReader value1]

2.2.2 关键的代码实现

  • 原始数据项调用字串使用数据通路的数据项绑定
# 定义个全局的字典
allreadpubvalue = {}
# 原代码行 NSString value1; 4.1生成的类型及变量名
matchObj = re.match(r"(.*)\s+(.*);", line, re.M|re.I)
if matchObj:
    # valuename = value1
    valuename = matchObj.group(2) 
    # key = [XXXSetting share].value1
    key = '[XXXSetting share].' + valuename
    # value = SettingReader value1] ,不同的模块再加上[XXX
    value = 'SettingReader ' + valuename + ']'
    # 赋值 key = [XXXSetting share].value1,value = SettingReader value1] 
    allreadpubvalue[key] = value
  • 查找原调用方式,升级为数据通路的读取方式
# 获取当前工程中,所有源码文件及对应的组件名
allfileandlib = {}
# allpubvalue 全局变量,字典
for key, value in allreadpubvalue.items():
    # filename为文件名,libname为组件名
    for filename, libname in allfileandlib.items():
        # 当libname为XXX replacevalue = [XXXSettingReader value1]
        replacevalue = '[' + libname + value
        # 实现个函数 重写这个文件 将文件中 [XXXSetting share].value1 替换为 [XXXSettingReader value1]
        reWriteFile(filename, key, replacevalue)   
  • 文件重写函数实现,需要实现全字的匹配,避免数据中存在相互为子串的情况。
# 定义一个输出的数据,初始为空字串
outfiledata = ''
# 使用正则全字匹配,查找替换
regAbKey = fromstr.replace('[', '\[')
regAbKey = regAbKey.replace(']', '\]')
regAbKey = regAbKey.replace('.', '\.')
# \[XXXSetting share\]\.value1\b
pattern = r'' + fromstr + r'\b'
# 依次从文件中读,正则全字查找及替换
for line in f:
    newline = re.sub(pattern, tosrt , line) 
    outfiledata += newline

03 小结

本篇是本系列的最后一篇,在本系列第一篇内容中介绍了通过Python脚本实现公开接口及调用关系的分析,用来支持重构工作量及影响面的评估。在第二扩篇内容中,介绍了通过Python脚本实现数据项提供模块接入数据通路的代码转换。

本篇内容介绍使用 Python 编写自动化工具,实现了将原数据项使用模块中直接对数据项提供模块中数据项的读写方式,升级为通过数据通路间接读写。包括每个数据项读写类的封装和数据项使用模块的调用代码段适配。通过封装每个数据项的读写类,并为每个数据项封装了独立的读写函数,和对原有调用代码的自动替换,这些工作是IDE提供的相关工具不可支持及定制的,基于Python编写的自动化工具,降低了重构成本,并在测试及上线阶段实现了零 Bug。

欢迎加入百度搜索大前端团队,持续招聘 iOS/Android/Web前端 研发工程师。简历欢迎投递至joinefe@baidu.com

——END——

推荐阅读

百度搜索深度学习模型业务及优化实践

文生图大型实践:揭秘百度搜索AIGC绘画工具的背后故事!

大模型在代码缺陷检测领域的应用实践

通过Python脚本支持OC代码重构实践(二):数据项提供模块接入数据通路的代码生成

对话InfoQ,聊聊百度开源高性能检索引擎 Puck

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1215047.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

还在为忘记BIOS密码担心?至少有五种方法可以重置或删除BIOS密码

忘记密码是一个我们都非常熟悉的问题。虽然在大多数情况下,只需单击“忘记密码”选项,然后按照几个简单的步骤即可恢复访问权限,但情况并非总是如此。忘记BIOS密码(通常为避免进入BIOS设置或避免个人计算机启动而设置的密码)意味着你将无法完全启动系统。 幸运的是,就像…

非对口专业测试人,婉拒猎头、放弃6份高薪offer,你敢信?

从非对口的国贸专业,步入测试之路;从红色旅游小城湘潭,迈入国际化都市上海。“明确方向-及时实践-谨慎选择-踏实扎根-计划未来”。她的每一步,都走得格外坚定有力......话不多说,让我们一起来看看这位小姐姐的成长故事…

揭秘2023年最热门的跨境电商源码趋势,你不能错过的关键信息

随着全球市场的不断扩大和国际贸易的加速,跨境电商源码正成为越来越多企业的首选。在本文中,我们将揭秘2023年跨境电商源码的最新趋势,为您带来关键信息,帮助您抓住机遇,实现商业成功。 2023年跨境电商源码趋势解析 …

JavaWeb-CSS

一、什么是CSS CSS(Cascading Style Sheets,层叠样式表)能够对网页中元素的位置排版进行精确的控制,拥有对网页对象和模型样式的编辑能力,简单来说就是页面美化。 CSS样式代码中的注释需要使用/**/。 二、CSS的引入方…

未来服务器操作系统的趋势与展望

摘要: 随着云计算、大数据和人工智能不断的发展,服务器操作系统也需要随之进行新一轮的升级。本文通过分析当前服务器操作系统的现状,探讨了未来服务器操作系统的趋势和展望,并针对一些关键问题提出了解决方案。 一、引言 服务器…

最全100种副业汇总,如果你没一份稳定的副业,那么要仔细看看了

为什么一定要有一份副业?三种人一定要有一份自己的副业,宝妈、大学生、死工资的上班族。因为有了一份优质的副业,您将不用依靠老公生活,您将不用向父母伸手要钱,您将不用时时刻刻看领导的颜色。 今天为大家总结了各类副…

折爱心教程(简单版本)

文章目录 1.折出双三角形2.向中心折叠3.形成正方形4.对折正反面相同5.向中心折6.外侧角向中心折7.顶部三角形向下折叠注意参考资料 我怎么也没有想到,身为混迹职场多年的老油子,竟然还能遇到折纸这种硬性task。 可是给的教程步骤省略太多了,看…

自动驾驶大模型,是怎么学习「世界知识」的?

近期,科技产业大佬不约而同地发出一个非常强烈的信号:自动驾驶走向完全的成熟,必须要被AI大模型重构。 中国工程院院士、清华大学教授、清华智能产业研究院(AIR)院长张亚勤认为,「自动驾驶是高度复杂的、最…

《QT从基础到进阶·二十九》QT,opencv源码调试

有时候我们在使用VS调试程序的bug,但发现程序崩溃的地方并不在我们写的程序中,我们通过调用堆栈发现程序崩溃的地方出现在QT或者opencv等源码中,那么我们怎么能把断点打到这些开源库中,下面提供一种办法: 解决方案–右…

老哥们平日是怎么排查线上问题的?

1、做好监控告警 如果线上出现了问题,我们更多的是希望由监控告警发现我们出了线上问题,而不是等到业务侧反馈。所以,我们需要对核心接口做好监控告警的功能。 2、定位报警层面 如果是业务代码层面的监控报警,那我们应该是可以…

2023 年 数维杯(C题)国际大学生数学建模挑战赛 |数学建模完整代码+建模过程全解全析

问题重述 信息技术和人工智能的迅速发展,特别关注大型语言模型(Large Language Models,LLMs)在全球范围内的广泛应用,以ChatGPT为代表。这些模型在机器人导航、语音识别、图像识别、自然语言处理和智能推荐等领域表现…

Dread Hunger恐惧饥荒服务器宣布关服

Dread Hunger恐惧饥荒服务器宣布关服 大家好我是艾西,如果不是被逼无奈 谁不喜欢在自己的舒适圈待着呢.从2022年年初到2023年下半年有太多的记忆了从最开始的当狼激动开心到后面慢慢的变成了一种恐惧,从最开始的变态花活变成了只要赢就行逐渐麻木了 作为…

Ubuntu 安装VMware Tools选项显示灰色,如何安装VMware Tools

只要你的网络没问题,你直接执行这几个命令,重启ubuntu虚拟机即可、 sudo dpkg --configure -a sudo apt-get autoremove open-vm-tools sudo apt-get install open-vm-tools sudo apt-get install open-vm-tools-desktop

苹果电脑录屏快捷键,让你成为录屏达人

“苹果电脑录屏好麻烦呀,操作步骤很繁琐,有人知道苹果电脑怎么快速录屏呀,要是有快捷键就更好了,大家知道苹果电脑有录屏快捷键吗?谢谢啦!” 苹果电脑以其直观的用户界面和卓越的性能而闻名,而…

MES选型注意事项

1、明确需求和边界 企业首先要明确需要MES哪些模块及要实现的目标,如实现质量透明化、设备状态管理、IPQC检验管理等。MES通常要与其他系统集成,如与ERP集成,要事先明确哪些在ERP中做,哪些在MES中做,哪些需要同步。 车…

java VR全景商城免费搭建 saas商城 b2b2c商城 o2o商城 积分商城 秒杀商城 拼团商城 分销商城 短视频商城

1. 涉及平台 平台管理、商家端(PC端、手机端)、买家平台(H5/公众号、小程序、APP端(IOS/Android)、微服务平台(业务服务) 2. 核心架构 Spring Cloud、Spring Boot、Mybatis、Redis 3. 前端框架…

普源DS1052E固件升级【附所有升级固件及工具】

折腾了两天,总算是弄好了。 升级的目的是啥?DS1052E的带宽是50M,示波器的时基最小可以调到5ns。固件升级后示波器的时基最小可以调到2ns,理论上说明此时示波器的带宽是100M。 网上的方法能找到很多,我总结一下大概的流…

23款奔驰GLC260L升级小柏林音响 全新15个扬声器

2023年款奔驰GLC260 GLC300升级小柏林之声 3D音效系统 升级小柏林之声音响之后,全车一共有15个喇叭,1台功放,每一首音乐都能在车内掀起激情的音浪,感受纯粹的音乐享受,低频震撼澎湃,让你的心跳与音乐完美契…

【2021集创赛】Arm杯二等奖-基于Arm核的智慧病房手势识别方案

团队介绍 参赛单位:上海交通大学 队伍名称:芯灵手巧 指导老师:王琴、景乃锋 参赛队员:林圣凯、林新源、莫志文 总决赛奖项:二等奖 1.项目概述 1.1 选题背景 我们的选题背景是考虑到很多卧床病人不便于独自向医护人…

前端实现RSA非对称加密及生成RSA公私密钥

前端实现RSA非对称加密 RSA简介安装jsencryptRSA加密RSA解密如何生成公私秘钥(windows) RSA简介 RSA用于保密性时,就是公钥加密,私钥解密。 因为公钥是可以公开了, 那么任何人都可以使用公钥对信息进行加密&#xff0…