如果你可以站到每一位框架使用者的肩上去编码和解释它应该如何使用,那么本书中的准则就都是多余的。作为框架作者,本书介绍的准则为你提供了一系列工具,使你能够创造出介于框架作者和框架使用者之间的通用语言。例如,将操作作为属性提供给用户,而不是公开一个方法给用户,这可以向用户传达应该如何使用该操作的信息。
在PC时代初期,开发应用的主要工具是语言编译器,一个很小的标准库集合和操作系统应用编程的原始接口(API),这是一套非常基础的编程工具集。
开发者使用如此基础的工具来开发应用,在这一过程中,他们发现了越来越多的重复代码,这些代码可以通过更高阶的API进行抽象。操作系统供应商发现,如果他们可以为开发者提供这些更高阶的API,那么就可以让开发者以更低的成本为他们的操作系统开发应用。这样,在其操作系统上运行的应用数量就会增加,就更利于操作系统去吸引那些需要各种应用的终端用户。同时,独立的工具和组件供应商也迅速意识到提升API抽象级别所带来的商机。
同一时期,业界慢慢开始接受了面向对象的设计,并认识到它在可扩展性和可复用性方面的重要性(面向对象语言不是唯一可用于开发可扩展、可复用库的编程语言,但是它们在普及可复用性和可扩展性这一该奶奶的过程中扮演了关键角色。可扩展性和可复用性是面向对象编程(OOP)哲学中的重要组成部分,对面向对象编程的采用反过来也增加了人们对其好处的认识)。当可复用库供应商采用面向对象编程(OOP)来开发其高级API时,框架的概念就随之产生了。即,应用开发者不再从无到有地开发应用,框架为其提供了大部分所需地代码,然后开发者可以对其进行自定义和连接(现在有很多关于面向对象(OO)设计的批评,其声称OO所承诺的可复用性没有实现。面向对象设计并不是可复用性的保证,我们也不确定它是否曾承诺过。然而,面向对象设计提供了自然的结构来表达可复用单元(类型)、控制可扩展性(虚成员)并促进解耦(抽象))以形成应用。
随着越来越多地供应商开始提供组件,这些组件可以被拼接到同一个应用中实现复用,开发者发现一些组件并不能很好地组合在一起,他们的应用看起来像是一座由不同的建筑工人建起来的房子,更糟糕的是,这些建筑工人之间完全没有交流!同样地,当更大比例地应用源码被用于API调用,而不是被用于书写标准地语言结构时,开发者开始抱怨现在他们不得不读/写多种语言:一种编程语言和几种可复用组件所使用的“语言”。这对于开发者的生产力来说有显著的负面影响——生产力是框架成功的主要元素之一。很明显,需要制定通用的规则,以确保可复用组件的一致性和无缝集成。
现在的许多应用开发平台都规定了一些设计约定,在为这个平台设计框架时必须遵循这些约定。如果框架不遵循这些约定,那么它们就不能很好地和该平台集成在一起,也会使得那些尝试使用它们的开发者扫兴,处于竞争劣势而最终失去市场。成功的往往是那些自洽、合理并且设计精良的框架。
1.1设计精良的框架的特质
那么,如何定义一个设计精良的框架,以及如何实现这个框架呢?有许多因素——如性能、可靠性、安全性、依赖管理等——影响软件质量。框架显然应当坚持相同的质量标准,但是框架与其他软件不同的是,框架是由一系列可复用的API组成的,这为设计高质量的框架提出了一系列特别的考量因素。
1.1.1设计精良的框架是简单的
许多框架并不缺乏能力,因为随着需求变得更加清晰,添加更多新的功能是相当容易的。相反,当计划压力、功能蔓延或者渴望去满足每个微不足道的边界场景占据了开发流程时,常常会牺牲简单性。然而,简单性是每一个框架必须拥有的特性。如果觉得当前的功能设计过于复杂,最好的办法就是把该功能从当前的发布版本中移除,在下一次发布前花更多的时间去做正确的设计。正如框架设计者常说的:“你总是可以新增,但是永远不能移除”。如果感觉设计不合适,你最好把它拿掉,不然你很可能后悔为什么没有这么做。
&&CHRIS SELLS(克里斯·塞尔斯)为了测试一个API是否“简单”,我喜欢将其提交给一个被我称之为“客户端优化编程”的测试。首先,尝试描述该软件库是做什么的。然后要求开发者更具他或她对这个软件库的想法(在没有实际查看你的软件库的情况下)来编写一个程序,看开发者所写出来的和你自己实现的是否大体一致。和不同的开发者一起多做几次这个测试。如果他们中的大多数人都写出了相似的代码,并且和你写的不一致,那么他们是对的,而你是错的,你应当适当地更新该软件库。
我发现这个方法非常有用,我在设计API的过程中,常常编写我所期望的客户端代码,然后实现一个软件库来匹配它。当然,你必须在简单性和试图提供的功能的固有复杂性之间取得平衡,这正是你的计算机科学专业学位的价值所在。
本书中所描述的许多准则都是试图在功能和简单性之间取得适当的平衡,特别是第2章,介绍了最成功的一批框架设计者所使用的一些基本方法,这些方法兼顾了简单性和功能。
1.1.2设计精良的框架设计成本高昂
优秀的框架设计并不是凭空产生的,它是花费了大量时间与资源,努力工作带来的结果。如果你不想在设计中投入真金白银,你就不要妄想可以创造出设计精良的框架。
&&STEPHEN TOUB 对于我们中那些发现自己参加的会议比理想情况下还要多的人来说,计算一次会议的成本或许是一个有趣且令人谦卑的爱好:房间里面的人数乘以与会者平均每小时预估的工资,再乘以会议的频率。在这种情况下,我参加过的最昂贵的会议是我们的.NET API审阅会议,我们审阅计划中的新API,并决定是否以及如何推进它。这些花费都是值得的,因为API设计中的不一致或者错误最终都会导致更大的负面开销,设计精良的、可被集成的API的价值“大于它们各个部分的总和”。
框架设计应该是开发过程中明确且独立的一部(不要误解,以为则会使对前置设计流程的认可。事实上,过度的API设计流程是一种浪费,因为API在实现后总是需要对它们进行调整。但是,API设计流程必须独立于实现流程,并且必须被纳入产品周期的每一个部分:计划阶段(哪些API可以满足用户需求)、设计阶段(为了得到正确的API,我们愿意在功能上进行哪些权衡)、开发阶段(我们有没有分配时间来试用框架,看看最终结果如何)和维护阶段(在框架的发展过程中,我们是否降低离设计质量。))。它必须是明确的,因为它需要适当的设计,调配人手,并最终被执行。它必须是独立的,因为它不能只是实现流程的部分;否则,常见的结果是,框架最终是由实现流程结束后,恰好保留下来的那些公开类型于成员所组成的。
最好的框架设计要么由明确负责框架设计的人来完成,要么由那些能够在开发过程中适时担任框架设计师角色的人来完成。混淆职责是错误的,会导致在设计中暴露实现细节,而这些细节对框架的最终用户来说本应该是不可见的。(原型是框架设计流程中最重要的部分,而且原型和实现非常不同。)
&&JEREMY BARTON 作为领域专家,要产出一个好的API也是非常困难的。你了解问题空间究竟有多复杂,你的提案已经把问题简化到了本质——这是简单性的巅峰。然而,事实并非如此,那些不熟悉细节的人依然会告诉你它太复杂了,并且它们很有可能是对的。
即便是那些由.NET API审阅团队成员所提出的API提案,在审阅过程中也会发生变化。在API审阅过程汇总引入那些在这个功能领域不是专家的人,可以显著提高任意API提案的质量。
1.1.3设计精良的框架充满权衡
世界上并没有完美的设计。设计就是要做出取舍,为了做出正确的决定,你需要了解有哪些选项,以及了解它们的优势和不足。如果你发现自己在设计中不需要做出任何取舍,那么极有可能是你忽略了某些重大的问题,而不是找到了“银弹”。
本书中描述的实践是作为指导性原则(Guideline)而不是作为规则(Rule)提出的,这正是因为框架设计需要管理权衡。其中的一些准则讨论了所涉及的权衡部分,甚至为特定的场景提供了替代方案。
1.1.4涉及精良的框架会借鉴过往经验
大多数成功的框架都会借鉴现有的成熟设计并在此基础上进行构建。或许,在框架设计中引入全新的解决方案是每个人都梦寐以求的,但是那需要及其小心谨慎才有可能做到。随着新概念数量的增加,整体设计的正确性愈难保持。
&&CHRIS SELLS 请不要试图在软件库设计中进行“创新”,让你的软件库的API尽可能“乏味”吧!你需要做的是使功能(而不是API)变得有趣吸引人。
&&JEREMY BARTON 全新的解决方案最好还是留给那些交叉领域的问题。这有助于你了解新解决方案的真正价值,以及让你的用户理解为什么他们需要学习“额外的事务”。
改变是一件糟糕的事情,除非改头换面。只是在某个小的方面变得好一点儿,可能并不值得你的用户花时间去学习如何使用新方法。
本书中包括的准则都是基于我们在设计.NET核心库的过程中获得的经验的,它们鼓励借鉴那些经得起时间考研的事物,并让我们对那些没能做到这一点的保持警惕。我们希望你能以这些优秀的实践作为开始,并进一步改进它们。本书第9章介绍了大量可行的通用设计模式。
1.1.5设计精良的框架旨在不断发展
如何在未来发展你的框架?这需要你思考应该做出哪些取舍。一方面,框架设计者可以在设计过程中花费更多的时间和精力,有时候,额外的复杂度甚至可以用“以防万一”来形容。另一方面,仔细考量可以避免所引入的东西随着时间的推移而退化,甚至更糟,到后面不能保持向后兼容性(在本书中,并没有详细地讨论向后兼容性,但是它和可靠性、安全性和性能一样,也应被视为框架设计地基本要素之一)。通常来说,最好将新功能推迟到下一次发布,而不是在当前发布地版本中引入它。
任何时候,当你权衡一个设计时,都应当思考这个决定将会如何影响框架地后续发展。本书中提出的准则考虑到了这一重要问题。
1.1.6设计精良的框架是完整统一的
现代框架需要能够很好地与大量不同地开发工具、编程语言、应用模型等集成在一起,云计算和其他面向服务器地工作负载模式意味着为特定应用模型做框架设计的时代已经结束了,在框架设计中无需思考合适的工具支持或者不需要与开发者社区所使用的编程语言进行适当集成的时代也已经结束了。
1.1.7设计精良的框架是一致的
一致性是设计精良的框架的核心特质,是影响生产力的最重要因素。一致性的框架使得开发者可以将知识从他们已知的部分转移到他们正试图学习的部分。一致性也能帮助开发者快速认识到,对于特定功能领域来说,设计的哪些部分是真正独特的,需要特别关注,哪些又是司空见惯的设计模式和惯例。
一致性差不多是本书的核心主题了,几乎每一条准则都部分地受到一致性的影响。其中的第3~5章大概可以说是最重要的章节了,因为它们都包含了涉及一致性的核心准则。我们提供了这些准则来帮助你成功构建框架。下一章介绍了一般软件库设计所涉及的准则。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?