一个模块究竟答应了调用者什么?
想换掉一个模块,却发现别处依赖它碰巧返回的顺序。从「接口写了什么」往前走一步:哪些决定本该藏在里面,又有哪些行为已经被人当成承诺?
本文的问题 · 7
少即是少处理例外与纠缠
API 设计演讲建议不确定时删去不必要内容;Simple Made Easy 将简单解释为关注点不纠缠;两者共同强调减少使用者必须理解的额外选择与依赖。
对照原文与时间戳 · 2
When it doubt, leave it out.start seeing interconnections between things that could be independent删减 API 选项只是一个设计手段;它不自动保证底层关注点已解耦。
用小接口和深实现减少使用者负担
Bloch 建议 API 保持小而清晰,拿不准的功能先不加入;Ousterhout 的深模块则以简单接口承载大量底层功能。两者共同指向一种具体设计收益:把复杂实现留在边界之后,让调用者只需理解少量稳定概念。
对照原文与时间戳 · 2
When it doubt, leave it out.very simple interface with a very large amount of functionality underneath it公共 API 的功能取舍与内部模块深度是不同层级的设计问题;小接口本身不保证内部模块设计良好。
小对象与小接口,不是同一个尺寸
Metz 谈类和方法的粒度,以及彼此知道得更少;Bloch 谈 API 暴露的功能范围,并建议拿不准时不加入。可以比较内部拆分与外部接口收缩这两个不同设计维度。
对照原文与时间戳 · 2
make smaller thingsWhen it doubt, leave it out.更多小类不必意味着更好的公共 API;不能把两段合并成对任意粒度的越小越好。
模块边界既保护演进,也冻结选择
Conway 演讲指出沟通成本越高,跨边界迭代越少;Bloch 指出 API 一旦有人使用就难以删除。合读可以把变更成本拆成跨团队协调和对既有调用方的兼容承诺,分别检查设计边界的代价。
对照原文与时间戳 · 2
the higher the cost of communication the less iteration will happen therenot be able to take it out once you put it into an API.组织沟通成本与公共 API 兼容性是不同约束,不能把所有接口稳定性问题归因于组织图。
可观察的实现细节会变成兼容负担
Python的slots、类型与模块缓存被生态依赖,阻碍实现调整;Swiss Table演讲将同一现象明确表述为用户会依赖所有可观察行为,并用随机化抑制偶然迭代顺序成为契约。
对照原文与时间戳 · 2
people actually depend in very bizarre ways on some of the internalsanything a user can observe about the implementation of your API不是所有内部行为都必须隐藏;Python演讲同时肯定栈状态暴露带来的调试收益。
使用者依赖了可观察行为之后,实现细节就可能变成兼容性承诺
解释器、哈希表和公共 API 是不同场景;能观察到某种行为,不等于它已被正式保证。
对照原文与时间戳 · 3
people actually depend in very bizarre ways on some of the internalsthey will rely upon
it, given enough users.not be able to take it out once you put it into an API.模块名称限制多版本,内容哈希允许共存
Python演讲指出sys.modules以模块名定位对象,现有代码与pickle依赖这一语义,因此同库多版本难共存;Unison则把引用绑定到内容哈希,区分同名但不同定义,使多版本依赖无需争夺全局名字。
对照原文与时间戳 · 2
we would have to make S modules also have a version identifierthen it would be perfectly fine to allow as many different versions of the same library in our code baseUnison不是对Python问题的补丁;类型在边界相遇时仍需要转换,运行时与生态不同。
读完再想一步
挑一个准备修改的接口:哪些行为有明确约定,哪些只是碰巧一直没变?