写点什么

Flash 务实主义(七)——构建易维护的程序:高效修改

  • 2011-06-10
  • 本文字数:3156 字

    阅读完需:约 10 分钟

一般程序开发完成后就进入了繁琐无趣的后期维护阶段,请不要以为一个不停更新的项目后期维护是一件轻松的事情,它会暴露出开发过程中的所有硬伤,不规范的写法、混乱的逻辑结构、高耦合导致地牵一发而动全身。虽然开发内容实际上减少了,但人力成本反而更高。

要提高这方面效率有很多技巧,本文介绍的内容只是起点–如何快速找到项目中需要修改的代码。

一般出现问题首先看到得是表现部分,例如对话框,关系到一些具体逻辑或某个服务端请求,即使不是很清晰的部分也一定有临近的区域。根据表现找到其对应代码,我将其称为定位。

搜索关键字:泛用但低效

搜索关键字是广泛使用的方法。例如,你在节目上看到某个图片,找到图片标志,在所有代码中搜索图片标志,一定可以找到调用这个图片的代码。再如,屏幕中显示的文本,也能找到对应的语言包标识,找到相关代码。然而,这种做法效率很低,因为你要找到标识的具体拼写,搜索项目代码查找关键字也需要时间。所以下面主要介绍如何不借助搜索直接找到目标代码。

包结构

类一般可以从两个维度分类,一个维度是结构类别,如模型、视图、控制器,甚至工具类、组件,另外一个维度是业务类别,如商店、人物、战斗等各种不同模块。

目录里的文件只有一个根,类似单继承,所以你只能用一个分类做为大类别。一般情况都是用结构类别做为大类别,因为结构类别一般是固定不变地,而业务类别可能会经常变化。例如下图所示情况:

这样做是为了避免大量类混杂在一起,只有分到不同目录才能彻底解决这个问题 (目录可折叠,必要时子目录或文件命名可重复)。开发模块时,只需展开关心的目录,避免其他文件干扰。

文件命名

推荐文件名根据结构类别做前缀,如视图以 UI 开头、后台请求以 Rpc 开头等。如果没有前缀,命名时很容易遇到重复的情况。再以业务类型设定第二个前缀,使得没有目录时,按字母排序时同一系统中的类被排到一起,当然也可以防止重名。

恰当的命名会在利用代码提示引入类时提供便利,而且在打开类(ctrl+shift+T)对话框里也比较容易找到需要的类。当然,主要还是在查看包资源时,列表会比较整齐,方便找到特定文件。

以上是为了帮助你在知道类的功能和类别却不确定具体命名时使用的,你可能不记得具体名字,但应该可以判断出它所在的包,但仅仅这样是不够的。

逻辑分离是前提

逻辑要按一定规则分开到不同类中,否则你的查找目标本身就不存在。

MVC 是实现逻辑分离的方法之一。根据 MVC 思想将类分开,你就会很清楚知道,与用户交互、显示有关的类是在视图中,与数据格式转换、获取特定数据 (诸如获得图标实例)、判断 (诸如 isPropTask() 之类)、修改数据逻辑 (诸如修改经验值触发升级) 是在模型中,而制定特定服务器请求、设置模型数据、引起数个视图更新的代码一定是在服务器请求类的 result 中。这样就能确定目标位置,即使不能确定具体某个类,也能界定到某个包范围内。

当然这是需要事先约定的,但只要开发者理解并遵守这个约定,就可以做到不依赖搜索关键字也能立即找到需要修改的代码。毫无疑问这种做法是值得的。

从视图着手

视图是最容易找到的部分,然后根据交互事件模拟用户操作过程,通过调用关系一层层查找,最后就能找到需要的部分。

无论你的代码结构如何,这种方法都是通用可行的。如果你的目标就是视图,需要修改的是诸如布局、颜色、数据填充逻辑,这样做就可以了。但如果你的目标不是视图,这并不是最好的办法,因为毕竟需要从视图一层层中转,会多几个步骤而不是直接找到,这自然影响效率。

模型

模型不只是数据。

很多人不明白为什么要在数据之外套一层模型。代码放哪里比较好要以可复用性做为标准,放在模型里的逻辑应该是和数据密切交流的,最基本的是数据序列化和反序列化。这些内容不少人认为应该写在请求完成函数里,而实际应该写在模型中,因为同一个模型可能会由不同请求生成,它们传入的数据格式是一样的,只有在模型中解析才能重复利用这段功能代码。

此外模型还需要负责和数据相关的逻辑,以某个 RPG 游戏为例:

  • 人物属性变化:包括金钱变化(钱不够会失败并提示充值),经验变化(可能需要升级),还有数据更新后对应视图更新;
  • 道具管理:增加 / 删除道具,获得道具数量,判断道具满;
  • 计算:诸如保存地图模型可以提供计算 A* 的方法;

上述逻辑是针对游戏玩家的,只会存在一个模型,数据模型是固定的,所以无论放在哪里只要集中都容易查找,但显然放在模型里最好理解,而且这样即使出现多角色需求,需要修改的地方也很少。

模型通常也会提供一些简单的数据转换方法,例如:

  • 获得图标,类似的方法有获取格式化文本、获得 ToolTip 信息等;
  • 校验,一般一组条件的与 或关系或者大于 等于 小于判断,也可能有循环遍历数据进行统计等复杂表达式;

如果按照约定编码,将这些逻辑存放在模型中,这样就不能通过视图快速找到代码,但模型数量少、逻辑少,会比放在视图里找快很多。你也可以用查找引用方法找到调用模型方法的代码片段,以确定这个逻辑的入口,方便逆向追踪。这样只需修改一处,所有相关部分都会发生变化。

命令(Command/Action)

所谓命令,其实很大一部分都是用来请求服务端数据的,设计中有 result 方法可以在返回结果后处理一些事情,例如整理数据格式、设置模型、调用视图更新方法。虽然很多人将这些功能发明放在视图中,但显然放在 Command 里重用率更高且更易于定位。

服务端返回的数据会经常变换,虽然具体解析是在模型中完成,但返回的数据常常是多个数据拼凑在一起,可能是数组或者一些简单数据类型,这部分可以有 Command 处理,做特殊解析并给相关模型赋值。由于这部分需要与服务端沟通,是容易出错的地方,放在 Command 里容易找到,修改时也可以省去不少麻烦。

命令除了给模型赋值,也还会有一些触发操作。如果确定某个逻辑是请求返回一定执行的也可以放在这里。例如更新视图、触发附加逻辑、首次购买某物品的弹窗。这部分常发生变化,放在 Command 执行也易于调整。当然 Command 也可以与服务端请求无关,但道理是一样的。

总而言之,原则是尽可能不要将代码放在视图,而是放在联系更紧且数量少、代码少的一方,这样就能更快找到修改代码位置。Command 一般用来调用模型和视图方法,是其他逻辑的入口,自己只有少量代码。

常量类

配置类数据只可能存在于三个地方:服务端,本地配置文件,常量类。

对于服务端数据配置,客户端只单纯接受,本地配置文件一般都保持经常更新的大量数据,所以零散数据配置通常都存在常量类里,例如等级上限、各级经验分配、特定功能花费。

很多开发者都喜欢偷懒,例如一个功能需要花费 5 元开启,开发者直接在代码里写 5。这样看起来算不算神秘数字先不谈,要知道,这种数据即使说绝对不会变而它未来变化的几率也会超过 30%。所以常量是必须有的,哪怕数值是 1,也应该写成常量,因为未来可能变成 2,在代码里保留数字始终会有隐患。如果你设置为常量,对于这类内容就可以直接找到常量修改,而不用关心其他部分,也很容易找到使用这个常量的代码片段。如果没有常量的话,就只能借助和这个数值相关的内容引导并借助关键字搜索以确保修改无遗漏了。

此外,有多版本时配置可能会变化,如果硬编码写死在程序中,需求会很难实现。

其他

这里介绍是以通用 MVC 为例,因为熟悉的人较多,容易理解。游戏中除用户界面外,结构会更复杂,虽然也有类似模型视图这样的概念,但层次需要分得更细致。其实这些分离方法都是约定的,既然要分离,就要更合理、更易于理解、更具有可复用性。高效修改首先要容易找到问题症结点,要达到这个目的,就要求代码结构整理。良好的结构也可以让约定更加简洁,易于记忆和理解。

上述这些内容,框架并不能帮到你,因为框架大多是限制你分成几部分,而没有也无法限定这几部分具体是什么内容。因此你需要刻意地约定,而这个刻意地行为,对于减少修改维护时的人力成本,比框架重要百倍。

2011-06-10 18:381918

评论

发布
暂无评论
发现更多内容

弱网下的极限视频通信学习感悟!

txp

音视频

First Unique Character in a String 的变种问题返回第一个找到符合条件的字符

HoneyMoose

一房地产数据服务初创公司的面经

HoneyMoose

uni-app rtm插件集成指南及常见问题--iOS

anyRTC开发者

uni-app ios 音视频 WebRTC sdk

耗时5小时,用低代码搭了2套应用,我才明白它为什么能火了

优秀

低代码 低代码开发 低代码开发平台 低代码平台

GitHub开源的中国亲戚关系计算器

不脱发的程序猿

GitHub 开源 程序员 4月日更 中国亲戚关系

迪安精选:那些好用的浏览器扩展

迪安

浏览器 插件 扩展

ceph-csi源码分析(4)-rbd driver-controllerserver分析

良凯尔

Kubernetes 源码分析 Ceph CSI

golang单元测试踩坑系列(一)

geange

单元测试 Go 语言

北美一工作搜索引擎公司技术岗面经

HoneyMoose

ceph-csi源码分析(3)-rbd driver-服务入口分析

良凯尔

Kubernetes 源码分析 Ceph CSI

中国区块链产业全景图

CECBC

技术应用

浙江宁波市区块链研究机构发布首个全国性公证联盟运营链

CECBC

区块链

Python3 print变量打印输出功能后面隐含的几个知识点

老猿Python

Python print str repr

软件 IT 专业大学生职业方向情况调查

李孟聊AI

大学生日常 IT 大学生

量化策略倍投系统搭建,马丁策略交易

抵制羊毛党,图计算“加持”互联网电商风控

华为云开发者联盟

风控 图计算 互联网电商 羊毛党

LeetCode题解:191. 位1的个数,位运算,JavaScript,详细注释

Lee Chen

算法 大前端 LeetCode

客服中心简单分析

zzz

vue+webpack+vue-cli

Vue js 打包 webpack vuecli

Faiss源码剖析:类结构分析

华为云开发者联盟

机器学习 KNN Faiss 类结构 Quantizer

SpringSecurity+JWT认证流程解析

学Java关注我

Java 编程 程序人生 计算机 架构】

NumPy之:数据类型对象dtype

程序那些事

Python 数据分析 Numpy 程序那些事

人类视觉神经科学助力音视频产业革命-弱网下的极限实时通信

张音乐

音视频 笔记 弱网下的极限实时视频通信

让宝妈宝爸告别安全顾虑,区块链构建母婴行业新生态

CECBC

母婴

飞桨中国行落地合肥,与当地企业共话产业智能化升级

百度大脑

智能化 飞桨中国行

云图说|ModelArts Pro,为企业级AI应用打造的专业开发套件

华为云开发者联盟

AI 企业应用 ModelArts Pro 开发套件

【一定要看哦】转瞬之间的成长,传播知识的梦想(赠予极客邦【1周年】)

码界西柚

程序人生 1 周年盛典 InfoQ 写作平台 1 周年 InfoQ 的朋友们

图的学习总结

Nick

数据结构 数据结构与算法

Kubernetes 上如何控制容器的启动顺序?

张晓辉

Kubernetes istio

一文带你了解华为云GaussDB的五大黑科技

华为云开发者联盟

数据库 华为云 GaussDB(for Influx) 时间线 tpmC

Flash务实主义(七)——构建易维护的程序:高效修改_Java_flashyiyi_InfoQ精选文章