注:这是一篇没有什么营养的吐槽文

清理工具

自从一年多前删了一批程序发现右键上下文菜单留下了一批钉子户之后就想着哪天有时间写个工具清理一下。

但是因为比较懒就一直拖到了现在;并且在某天脑袋被门撞了之后想着要不顺带做一个 GUI 吧…

因为核心需要操作注册表,免不了一顿 Win32 API 调用,所以自然用 C++ 比较方便,但是用 C++ 写 GUI 吧,稍有经验的人都知道这是一个蛋疼的事情。

还好我还算比较有毅力:

  1. 用了一个晚上基于 KBase 封装的基础工具写完了核心逻辑
  2. 在网上找了一个比较轻量的 nana-gui 糊了一个周末的 UI

最后算是把成品给做了出来,也确实达成了我的目的,清掉了残留的菜单项。

完整工程链接:the-stupid-context-menu-cleaner

GUI? Thanks, but NO

这部分又名 我为什么不喜欢写 UI。

说起来我的编程生涯是从 GUI 程序开始的。

初中的时候各种折腾 VB6,一直想做个好看、高大上的应用;经常为了追求实现一些炫酷的效果大规模使用当时口口相传的秘术:子类化(sub-classing)。

说是秘术无非是 VB6 本身不支持这样做,强行通过 Win32 API 接管被内部封装的 wndproc(窗口的消息处理过程);记得很容易因为一个不小心连带 IDE 都自动退出。

并且那会儿也没有所谓的 layout management,全靠可视化界面编辑器一个一个拖控件,然后通过手写 on_resize 事件根据窗口大小来调整控件的布局。

即使到了高中开始学 C++ (with classes),也是想着怎么在 MFC 里用那些蹩脚的基础控件和机制把应用做得漂亮点。

直到升入大学并在 C++ 和 system programming 这条歪路上越走越远,GUI 编程对我的吸引开始日益减少;直至今天我对这部分事情完全提不起兴趣。

导致这个变化的原因粗想了一下大概有下面几个。

我喜欢写 C++ 然而 C++ 不适合写 GUI

虽然十几二十年前有相当一部分桌面程序是 C++ 写的,但是系统级编程语言的定位实际上并不适合 GUI 开发。

其一是 C++ 的语言优势在这个领域不仅派不上用场,更多时候反而是个包袱。

试问,GUI 编程什么时候需要 zero-cost abstraction 呢?写的又不是渲染引擎。

而 C++er “是个人都要造轮子”的习惯和“我都写C++了能不能做点有技术含量的”的风气导致大家更倾向于自己做一个 UI 库而不是拿别人的 UI 库做 GUI 编程。

这就间接导致了另外一个问题:C++ 的主流 GUI 框架,从巨无霸的 Qt 到号称小而美的 nana,还有从游戏渲染得到启发的 immediate-ui,一双手都数不完,这还都是要求跨平台的。

更别说像 Chromium 这样的大象目为了为数不多的几个 UI 场景还自己做了一套 UI 框架。

可用框架太多且没有一个能够超出其它几个层次的就导致了下一个问题。

不同 GUI 框架/技术之间技术很难积累成经验

即使跨平台的 UI 框架尽力消除了平台之间的差距,但是框架之间的经验也很难平移。

对日常做 GUI 开发的人来说,如果没有压对框架,那么这部分经验大概率在后续工作中归零。

如果不巧工作上需要转型到前端,或者移动端开发,那么恭喜你,即使你过往对平台提供的 UI 底层机制了如指掌,这个时候也同样只能遗憾归零。

即使是前端,能够做到 vue 和 react 双修的也是少数;最一开始的 angular.js 也早已化为尘土。

记得 2014 年的时候我的直属 leader 让我学一下 angular.js 直接被我拒绝了,现在看看这个选择还挺正确的。

“过来人”会时常告诫你:不要针对框架 API 编程,不要把自己局限在框架里 .etc;然而残酷的事实就是,GUI 编程就是 framework-bundled programming。

为什么会这样呢?我想可能是下面这个原因。

GUI 编程的尴尬定位

除非你自己设计并维护一个广泛使用的 GUI 框架,否则 GUI 编程很难有领域范围的影响力;对其他领域更难造成什么 positive impact。

在传统的桌面应用开发团队里,甚至能看到专门负责 UI 部分的小组;而当平台更加统一并且提供了更多开箱即用的开发支持后,GUI 开发直接被融合到了普通的业务开发里。现在的移动端开发差不多就是这样一个状况。

随着 electron/cef,RN,flutter 这样的技术兴起,对原有的 UI 开发来说是一次洗牌,而不是融合。

类似的,除非你有能力直接在这些框架上根据自己需要开个洞,或者填补上一些洞,你永远只是这些框架浪潮的牺牲品。而这个能力靠 UI 开发是学不会的。

UI 开发是一件非常琐碎且繁杂的事情,尤其当你需要支持多个桌面平台,而做这些事情的背后却没有太多的收益;这就导致我个人对做这部分事情没有任何成就感。

个人成就感

好看漂亮的界面确实能让你感到愉悦,但是对我来说这并不是必须的。

产品的功能才是我用这个东西的目的;对于 product/application,我只是一个实用主义者,我不是一个美学大师或者哲学家。

对于美,我只关心代码的设计和实现是否足够美。

我写的代码就和诗一样,即使不能运行也很美。

Revisit Your Own Code

如果你是一个有追求的开发者,你应该时不时回顾自己以前写的一些作品。

只有这样,你才能体会到之前的自己对于一个东西的理解有多么的肤浅,写出来的东西有多么的挫。

对于有追求的开发者,“又不是不能用”这样的实用玩屎主义是要不得的。

KBase 中的注册表模块大约是我在2014年刚毕业那会写的。

时隔7年,我发现很多时候我都要看了内部实现才知道怎么正确的使用这个接口;完全没有做到 _interfaces should be easy to use correctly and hard to use incorrectly_。

同时,一些错误处理的设计也让我这次接入时大为光火:天晓得为什么一个基础类库内部会强制把一些错误给吞掉还输出到日志…

好几次我都在心里骂:我特么当年怎么写出这么垃圾的代码…

唯一令我欣慰的是,kbase::RegKeyIteartor 的设计确实遵从了 C++ iterator,而不是 Java/C# 那种披着 iterator 的 enumerator。

注:C# 倒是很干脆的管那些叫 enumuerator。

这使得我可以将 registry key 的迭代融合进 STL 的各色算法里

1
2
auto key = find_if(RegKeyIterator(HKEY, L"path"), RegKeyIterator{},
[](const auto& key) { ... });

而不是像 Java 那样只能傻乎乎的

1
2
3
4
RegKeyIteartor key(HKEY, L"path");
while (key.Next()) {
// do a lot of stuff
}

还得看完里面的 body 才知道做了啥。

文明是螺旋上升的,个人的成长也是。

很多几年前你认为是靠谱优秀的做法,用现在的眼光看去,可能就剩下尴尬。

当然我也知道现在墙内环境并不友好。大量廉价劳动力的涌入让职场变得异常内卷,而真心喜欢写代码并且打心里认为自己是 art craftsman 的始终是少数,而且还在不断的内卷中遭受冲击。

既然无法改变这个环境,那不如说服自己,工作和爱好完全可以脱钩,你工作只是为了积攒能够按照自己意愿生活的本钱。