拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

【WinForm 代码反脆弱系列】05 工程判断力 —— 这次重构教会我什么

【WinForm 代码反脆弱系列】05 工程判断力 —— 这次重构教会我什么

系列说明:这是《从一段"能跑但脆弱"的 WinForms 代码说起》的终篇。前四篇讲了线程、进度模型、重构分层、数据库操作,都是"术"。这一篇讲"道":技术之外,这次重构带来的认知升级是什么?工程师的 level 差距到底在哪?所有代码和业务均已脱敏。


文章目录

    • 一、 先讲一个真实的小故事
    • 二、 从"能跑"到"可维护"的鸿沟
    • 三、 工程师 level 的三个分水岭
      • Level 1:能实现功能(会写代码)
      • Level 2:能做出正确决策(会选方案)
      • Level 3:能沉淀方法论(会抽象)
    • 四、 什么时候信任框架,什么时候自己兜底
    • 五、 决策点上的"为什么选它,而不是另一个"
    • 六、 经验复利:这次的结构能套用到哪些场景
    • 七、 给 3 年前自己的三条建议
      • 建议 1:不要等到"必须重构"才动手
      • 建议 2:命名比实现更重要
      • 建议 3:每次解决一个问题,都问"这一类问题怎么解"
    • 八、 终篇小结
    • 系列回顾

一、 先讲一个真实的小故事

重构进行到一半,我发现原代码的备份逻辑是这样的:

publicstaticvoidBackUpDataBase(){// ...for(inti=0;i<tb.Rows.Count;i++){ExcuteSQL("rename table "+mDbName+"."+tbName+" to "+newDbName+"."+tbName,"");}// ...}

方法名叫BackUpDataBase(备份数据库),但实际执行的是rename table——把原库的表"搬"到备份库。

执行完之后:

  • 新库里有了数据 ✅
  • 原库里空了❌

这不是备份,这是迁移。

如果有人以为这是"备份"功能,某天点了一下,数据就没了。而且这个方法名还会让接手的人根本不会怀疑它有问题。

这一个 bug 教会我的,比前面四篇加起来都多。

它让我意识到:写代码这件事,真正难的不是"让它能跑",而是"让它在别人手里、在半年后、在意外情况下,依然安全"。


二、 从"能跑"到"可维护"的鸿沟

很多工作几年的人会有一个困惑:

“我写的代码明明能跑,功能都对,为什么老被说’代码质量不行’?”

因为"能跑"和"可维护"之间,有一条看不见的鸿沟。

用这次重构的代码举例:

维度"能跑"的代码"可维护"的代码
命名button1、ExcuteSQLbtnFixFlow、ExecuteSql
结构全塞在Form1三层分离
线程同步卡死,或DoEventsTask.Run+await
进度static progressMessage+ 定时器IProgress<T>
异常catch { return null; }分层处理
语义BackUpDataBase做 renameBackupDatabase做复制
连接每次现算连接字符串封装进DbHelper

"能跑"关注的是:现在、这台机器、这次操作、成功路径。
"可维护"关注的是:半年后、别人手里、异常情况、失败路径。

而工程师的 level 差距,主要就体现在你关注的是前者,还是后者。


三、 工程师 level 的三个分水岭

结合这次重构,我把工程师的成长分成三个 level:

Level 1:能实现功能(会写代码)

特征:

  • 拿到需求,能写出能跑的代码。
  • 遇到问题,会搜、会问、会试。
  • 关注点是"怎么把它做出来"。

典型表现:

// 需求:点击按钮,备份数据库privatevoidbutton1_Click(objectsender,EventArgse){BackUpDataBase();// 能跑,但 UI 卡死}

这个 level 的人,能完成任务,但代码是"一次性"的:换个人接手、改个需求、出个异常,就崩。

大多数工作 1~3 年的人在这个 level。


Level 2:能做出正确决策(会选方案)

特征:

  • 同一个问题,知道有几种解法。
  • 能说出每种解法的代价,而不只是"哪个好"。
  • 关注点是"为什么选它,而不是另一个"。

典型表现:

遇到"进度刷新"问题,Level 2 的人会这样思考:

方案优点代价适用场景
Invoke直接可控样板代码多,业务被污染更新点少
定时器 + 全局变量易上手全局状态、延迟、结束消息不可靠内部小工具
DoEvents写法简单重入风险临时测试
IProgress<T>线程切换自动化,业务干净需改方法签名推荐

Level 2 的人不会说"XXX 是最好的",而是说"在这个场景下,我选 XXX,因为……"。

大多数工作 3~8 年的人在这个 level。你这次重构,已经站到了这里。


Level 3:能沉淀方法论(会抽象)

特征:

  • 解决一个问题后,能抽象出解决一类问题的方法。
  • 能把自己的经验传递给别人。
  • 关注点是"怎么让团队、让未来的自己少踩坑"。

典型表现:

解决完这个项目的进度问题后,Level 3 的人会写一份《WinForms 进度刷新模型选型指南》,或者抽象出"重构七步法":

步骤做法
1. 先跑通确认现有行为
2. 识别边界UI / 数据 / 业务
3. 分离关注点拆类
4. 统一线程模型Task.Run+await
5. 命名反映语义改命名
6. 异常分层处理底层抛、中层记、上层提示
7. 小步验证每改一层编译一次

Level 3 的人,输出的不只是代码,还有"认知"。

工作 8 年以上的人,如果还在 Level 2,会很危险——因为你的竞争力只是"经验多",而经验是可以被时间追平的。真正拉开差距的,是"能不能把经验抽象成方法"。


四、 什么时候信任框架,什么时候自己兜底

这次重构里有一个贯穿始终的判断:什么时候信任框架,什么时候自己动手。

场景选择理由
连接管理信任连接池自己维护长连接会引入超时、线程安全、状态污染
线程切换信任Progress<T>手写Invoke容易出错
异步调度信任await手写ContinueWith是回调地狱
异常处理自己兜底框架不知道你的业务语义
备份语义自己判断框架不知道"备份"和"迁移"的区别
命名自己把关框架不会帮你起名字
分层自己设计框架不知道你的业务边界

规律:

  • 机制性的东西(连接复用、线程调度、异步调度)→信任框架。框架的通用实现比你自己写更靠谱。
  • 语义性的东西(业务含义、命名、分层、异常策略)→自己兜底。框架不懂你的业务。

“信任框架"不是偷懒,而是"不重复造轮子,还造得更差”。
“自己兜底"不是不信任框架,而是"框架不知道你要什么”。

这个判断力,是 Level 2 和 Level 3 的分界线之一。


五、 决策点上的"为什么选它,而不是另一个"

回顾这次重构,我做了这些决策:

决策点我的选择为什么不是另一个
进度刷新IProgress<T>定时器有全局状态、延迟、结束消息不可靠;Invoke污染业务代码
备份语义CREATE + INSERTrename是迁移,会导致原库丢失
连接管理短连接 + 连接池长连接会超时、线程不安全
代码组织三层分离全塞在Form1改一处动全身
错误处理抛异常 + 上层提示return null无法区分"空结果"和"出错"
控件命名btnFixFlow等button1半年后看不懂
备份进度带计数{index}/{total}不带计数用户不知道还有多久

每一处改动背后都有理由。这就是工程判断力的体现——不是"我知道怎么做",而是"我知道为什么这么做,也知道为什么不那么做"。

如果你能在技术评审时,对每个决策点说出"我选 A,因为 B,不选 C 是因为 D",你的 level 就藏不住了。


六、 经验复利:这次的结构能套用到哪些场景

这次重构的成果,不是"改完了这个项目",而是得到了一套可以复用的结构。

以后遇到类似的场景,几乎可以原样套:

场景可直接复用
WinForms 里做 Excel 导出Task.Run+await+IProgress<T>
WinForms 里做批量导入同上
WinForms 里做上传/下载同上
WinForms 里做长查询同上
WinForms 里做日志分析同上
任何"耗时 + 进度"的 WinForms 场景同上

数据库操作的结构也通用:

场景可直接复用
换数据库(MySQL → SQL Server)只改DbHelper
改备份策略(全量 → 增量)只改BackupService
加新数据操作在MainForm写 3 行,走DbHelper
写单元测试传假IProgress进BackupService

这就是经验的复利:一次重构的投入,会在未来 N 个项目里产生回报。


七、 给 3 年前自己的三条建议

如果能让 3 年前的自己看到这次重构,我会给三条建议:

建议 1:不要等到"必须重构"才动手

"能跑就行"是一个陷阱。

代码不是一次性的。今天写的button1,半年后就是自己的噩梦。每次写新功能时,顺手把"能跑"改成"可维护",比事后集中重构更省力。

建议 2:命名比实现更重要

命名是写给未来自己的文档。

BackUpDataBase做 rename 这件事,如果方法名叫MigrateDatabase,一眼就能看出问题。命名对了,很多 bug 在写的时候就能避免。

建议 3:每次解决一个问题,都问"这一类问题怎么解"

不要满足于"这次搞定了"。

这次解决了"进度刷新",下次遇到"批量导入"还是同样的问题。把每次解决的具体问题,抽象成"一类问题的解法",才是真正的成长。


八、 终篇小结

层次关注点典型问题
Level 1 能实现怎么把它做出来“这段代码能跑吗?”
Level 2 能决策为什么选它“为什么选 A 不选 B?”
Level 3 能抽象怎么让一类问题都好解“下次遇到类似问题怎么办?”

一句话总结:

工作 8 年,真正的核心竞争力不是"会写多少 API",而是在每个决策点上能说出"为什么选它,而不是另一个"。这次重构练的不是语法,是判断力——什么时候信任框架,什么时候自己兜底,什么时候该抛异常,什么时候该复制而不是迁移。这些东西不会写在任何教程里,只能从"改自己的旧代码"里长出来。


系列回顾

五篇走完,从技术到认知:

篇目主题关键词
第一篇线程 + UI消息循环、await、SynchronizationContext
第二篇进度刷新模型Invoke、IProgress<T>、定时器、DoEvents
第三篇重构分层、职责分离、重构七步法
第四篇数据库操作连接池、短连接、参数化查询、异常分层
第五篇工程判断力Level 分水岭、技术决策、经验复利

前四篇解决"怎么做",第五篇解决"怎么想"。

如果这个系列对你有帮助,欢迎转发给身边正在被"祖传代码"困扰的人。每一个 Level 2 的工程师,都是从 Level 1 的代码里长出来的。


系列完。

返回列表