Java写class时一口气塞给你一堆特性,Ada却让你像点菜一样逐个勾选——这种设计差异背后,藏着对"程序员到底需要什么控制权"的根本性理解分歧。

一个对比实验:从Java到Ada的引擎接口

打开网易新闻 查看精彩图片

原文作者的朋友递来一段Java代码,想看他怎么翻译成Ada。这本来只是个技术好奇,却意外暴露了两种语言哲学的鸿沟。

Java的做法很典型:先定义Engine接口,再让TwoStrokeV8两个类去实现。

Ada的翻译看起来类似,但结构已经分叉——它把"包规格"(package specification)和"包体"(package body)拆成两个独立文件。作者备注说代码有重复,但在大项目里,一眼能看到接口定义的价值远超那点冗余。

更关键的差异藏在类型声明里:

Java的class是个黑箱,继承、封装、多态、构造器……打包出售。Ada的type Two_Stroke is new Engine with null record却像在逐项勾选:继承Engine?勾上。需要额外字段?null record表示暂时不用。每个特性独立存在,你可以只拿多态不要继承,或只要封装不要虚函数。

"直到学Ada我才真懂面向对象"

作者的原话是:「I never truly understood object-oriented programming until I learned Ada」。

这句话值得拆解。很多人学Java/C++时,class是个魔法关键词——你按规则写,程序能跑,但底层机制是模糊的。Ada的拆解强迫你回答:我到底要哪个特性?为什么?

比如Java的接口实现,在Ada里被拆成"抽象接口类型"(abstract interface type)和"原语操作"(primitive operations)的显式绑定。overriding关键字不是装饰,是编译器帮你检查"这个Run确实覆盖了父类的Run"。这种显式性让错误在编译期暴露,而不是运行期踩坑。

作者提到这让他成为更好的程序员——我理解为:当你能清晰说出"我需要多态但不需要深继承链"时,你对设计模式的运用就从模仿变成决策。

细粒度控制的工程代价与收益

Ada的拆分不是免费午餐。代码量变多了,文件变多了,初学者的心智负担更重。但作者强调"在更大的项目里"这种结构的价值——我推测他指的是维护阶段的可读性和重构安全。

Java的class像精装房,拎包入住但改结构麻烦;Ada像毛坯+模块化家具,前期费工,但后期换地板不用砸墙。对于25-40岁、经历过凌晨两点改祖传代码的读者,这种权衡应该不陌生。

原文有个细节没展开但很重要:Ada的包规格"实际上被编译器用来帮助程序员"(actually used by the compiler to help the programmer)。这和C头文件的纯文本替换有本质区别——编译器理解Ada的规格,能做跨包的一致性检查,而C的#include只是文本拼接。

这件事为什么值得关注

语言设计正在经历一场"显式化"复兴。Rust的所有权系统、Go的接口隐式实现、甚至TypeScript的结构类型,都在不同维度上回应同一个问题:怎么让程序员对代码行为有更强的确定感?

Ada 1983年就做了这件事,只是圈子太封闭。它的"拆分哲学"提示了一种被忽视的可能性:面向对象不是必须捆绑销售的套餐,而是可以按需组装的基础设施。

对于正在设计系统架构的读者,Ada的案例有个直接启发:当你下次设计一个"大而全"的框架或中台时,能不能把它拆成可独立选用的原子能力?用户真的需要全部特性,还是只需要其中两三个?

最后留个开放问题:如果你现在维护的代码库能把某个核心抽象拆成Ada式的可选特性包,你会先拆哪个?是权限控制、日志追踪,还是数据校验逻辑?