什么是框架?






4.66/5 (63投票s)
尝试定义“框架”一词的含义。
前言
“什么更好?更聪明的程序员还是更安全的编程环境?”是促成本文的契机。在我(又一次)失言说 .NET 和 MFC 不是框架之后,Paul Watson 提出了两个显而易见的问题中的一个——“什么是框架?”(另一个问题是,“如果 .NET 和 MFC 不是框架,那它们是什么?”)嗯,Paul 的问题是一个绝佳的问题(而且我认为是两个问题中更好的一个),它引导我踏上了这条探索之路,试图“说到做到”。所以,开始吧。我很想听听您是否同意我的分析,您希望深入探讨哪些领域,以及您认为这是否符合您自己的经验。我试图让这篇文章保持简短(这是否令人耳目一新?),并将留给读者反馈来决定是否有需要详细阐述的地方。
引言
从我的角度来看,一个框架会做几件事情:
- 它使处理复杂技术更加容易。
- 它将许多离散的对象/组件组合成更有用的东西。
- 它迫使团队(或仅是我自己)以一种促进一致编码、减少错误和提高应用程序灵活性的方式来实现代码。
- 每个人都可以轻松地测试和调试代码,即使是他们没有编写的代码。
如果我看着这个模糊的框架要求列表,我会得到一组定义框架的具体分类。
- 封装。一个封装
- 简化了对技术的接口。
- 减少/消除重复性任务。
- 通过抽象提高应用程序的灵活性。
- 通常是可重用的,不考虑高级设计。
- 架构。一个架构
- 管理离散对象的集合。
- 实现一组特定的设计元素。
- 方法论:一个方法论
- 强制遵循一致的设计方法。
- 解耦对象依赖。
- 通常是可重用的,不考虑应用程序需求。
所以,在讨论框架之前,我们需要讨论封装、架构和方法论。在那之后,我不知道还有什么可说的了!
一个框架是...
封装
封装是一种重新包装函数或一组函数(无论是否相关)的方法,以达到以下一个或多个目标(可能不完整):
- 简化使用。
- 接口一致性。
- 增强核心功能。
- 将离散过程收集到逻辑关联(对象)中。
很容易陷入“万事万物皆封装”的观念,就像说“万事皆相对”(这是不真实的,因为这句话本身就是绝对的),但如果你仔细想想,并非万事万物皆封装。MFC 和 .NET 的大部分都是对核心 API 的封装。有些封装简直可笑,提供仅仅将消息包装成内联方法的类。其他封装则更复杂。例如,我为 Visio COM 对象编写了一个封装,它消除了使用 Visio 的原始函数(原始指“基本”的意思,而不是“实现糟糕”)来完成诸如下落形状、连接形状和读取形状集合之类的基本操作的繁琐工作。
但是,然后,你会遇到真正提供新功能的实现。是的,它利用了其他对象、其他 API,甚至其他封装,但它本身并不是一个封装,因为它做了一些全新的事情,而不是仅仅添加、删除或管理现有工作。封装会修改现有行为。有很多代码创建了新的行为(因此会受到
架构
架构是一种包含特定设计元素的风格。显然,框架需要有设计。它的架构与其实现的封装集合以及强制执行特定实现方法论是分开的。MFC 的文档-视图类是一种架构。本质上,架构实现了对象之间的关联——继承、容器、代理、集合等。架构有一个有趣的特点,如果你不喜欢它们,通常可以忽略它们或替换它们(至少在项目初期)。架构可以而且有用,因为它们创建了一个可重用的结构(对象集合),提供了增强的功能,但一旦你开始使用它们,你就基本上被困住了,除非你进行大量的重构。
方法论
让我们看看这个词:
- 方法——一种做事的方式。
- -学——以一种“科学”的方式——设计的、一致的、可重复的、可测试的、被证明的。
或者,如果你想在字典里查一下:*一个纪律领域的工作者所使用的实践、程序和规则的集合。*
好的,我们都接触过设计方法论,但很少有人接触过实现特定方法论的框架。我不认为争论 MFC 是一个方法论(有例外)是思考类的正确方式。虽然一个类指定了可见性、接口和继承用法,而这些,连同语言语法,当然可以被归类为“一套实践、程序和规则”,但说一个类或一组类是一个方法论,就像说一堆树叶构成了一棵树。方法论填补了支持结构。MFC 的消息映射实现是一个方法论吗?大部分是。虽然我主要将其视为对底层 API 的封装,并且如果你不想使用它,你也可以不使用它,但在某些情况下,你几乎无法避免使用它,特别是当你想要定义专门的消息处理程序时。你必须使用 MFC 实现的方法来指定和覆盖基类实现。而且,由于这是一个应用程序范围的问题,它比封装(它也是)或架构(它也是)更适合方法论的定义。所以,事情可能会模糊不清,有时感觉像是在吹毛求疵,但它不会减损将方法论视为一种分类的价值。
虽然架构处理事物之间的关联,但方法论处理事物之间的交互。前者是一种被动关系,后者是一种活动。我实现的大部分方法论都涉及对象之间的通信、数据持久性管理、响应用户事件等活动。在这些活动中,存在将相互关联的对象关联起来的架构。
设计模式
设计模式既是架构也是方法论。结构模式更偏向架构,而创建模式和行为模式更偏向方法论,因为它们的使用强制执行了一种特定的交互方式。无论你怎么实现行为模式,你的整个应用程序都必须遵循这种实现。然而,我得说一句——设计模式属于我称之为“轻量级方法论”的范畴。它们不一定对组件和对象如何相互交互的方式斤斤计较。
重量级框架
我曾写过的应用程序自动化层(你不会真的以为我写关于框架的文章而不提 AAL,对吧?)是我所说的重量级框架。它严格强制(在可能范围内)组件管理、数据交互、使用外部 XML 文件定义 GUI、脚本驱动函数式编程等。现在,你们可能会说这很过分,但我必须不同意。我们需要这样的框架来提高质量、一致性和可用性。此外,一个重量级框架可以(并且已被证明)让即使是初级程序员也能在最少的指导下高效地完成大规模开发工作。为什么?因为框架没有给“犯错”留下太多空间。即使是经验丰富的程序员,它也能帮助我避免犯错(例如,走捷径)!
关于框架的想法
相当黑白分明,不是吗?
在某个时候,框架仅仅是出于需要而成为三者兼备。你无法实现一种方法论而不实现封装和架构。与任何其他类型的编程一样,抽象概念不一定直接转化为具体的实现,封装-架构-方法论的观点也是如此。但是,将这两个主题分开来看仍然很有用,这样,在实现时,我们可以就我们正在实现的框架的目标做出更好的决定。例如,关于封装与架构与方法论的重用问题是不同的,因为重用的重点是不同的。对于封装,重用的重点是过程;对于架构,重用是对象之间的关联;而对于方法论,重用的重点在于对象之间的交互。
为什么框架要强制执行一种方法论?
嗯,我曾想避免使用建筑和房屋的类比,但这似乎是一个很好的地方,但我确定你们能猜到我接下来能说什么!那么,为什么要说呢?嗯,有时把想法写在纸上很重要,这样它就会变得更具体(无意双关)。如果你只有封装,你的团队(或你一个人)就只能按照他们(或缺乏)的经验来构建应用程序。最后(即使你能完成),你会得到各种风格、方法和解决方案的集合,它们没有任何一致性。这很难调试,很难维护,也很难扩展。而且,当你完成后,你几乎肯定不想再经历一次。相反,一个强制执行方法论的框架会告诉每个程序员如何做重要的事情,比如与其他对象/组件/技术进行接口,如何管理和持久化数据,以及如何避免跨越应用程序层(作为例子)。由此产生的应用程序易于调试,易于维护,并且非常灵活。
代码审查
代码审查很好,但如果用它来修复编码一致性问题,那就是误用。这就像健康一样——你想积极主动地预防疾病(不适,哈哈),而不是在生病后吃一把药。所以,代码审查应该用于预防疾病,而不是治愈有问题的实现。这意味着,代码审查应该始终关注框架方法论是否被正确应用,是否本身是合适的,以及代码是否在其他方面正确处理了其要求。
单元测试
如果你的方法论包括单元测试(因此需要一个框架来支持这种方法论),那么满足要求的问题就变成了预防性的——单元测试可以防止代码一开始就生病。然而,单元测试可能很像新时代用磁铁治疗关节炎的想法——它们可能完全没用。我不会在这里写关于好的单元测试的内容——如果你感兴趣,可以看我关于单元测试的文章。
敏捷方法
好了,这是一个敏感的话题,各个阵营都有非常固执的人。所以,我将回避这个问题,只说敏捷方法更多地讨论如何管理项目,而不是如何设计对象或实现实际代码。这就是为什么我认为它们帮助不大。我发现,几乎每一份工作、每一个客户或每一篇文章都需要一种独特的方法。没有一种一成不变的方法来处理客户、弄清楚应用程序需要做什么以及管理实现过程。然而,在每个应用程序的设计方式以及用于缩短实现时间的框架和封装方面,可以实现可观的重用。另一方面,敏捷方法作为你可以用来与客户或公司其他人员合作的想法非常有用。
创造力怎么样?
这个论点(信不信由你,我听过很多次)对我来说站不住脚。一个两岁的孩子可以用颜料和画布发挥创意,但你得不到蒙娜丽莎。一个训练有素的艺术家使用一种方法,让他能够将他的创造力引导到他选择的方法的限制内来创作他的作品。即使是杰克逊·波洛克也有他的绘画方法,尽管它们看起来有点像两岁孩子画的。关键是,一个好的方法论实际上让你从处理基本事情的日常任务中解脱出来,这样你就可以将你的创造力应用到更好的用户界面设计、更好的函数性能、通过使用线程提供的更流畅的用户体验等方面。结果是用户觉得美观的东西。作为一个程序员,我可以立即告诉你,当一个产品没有一个好的框架方法论时,它就会显得笨拙、粗糙、性能差,而且很可能充满了错误(也是最大的指标——交付时间比承诺晚了一年)。微软,你听到了吗?
一个例子:文档-视图架构
文档-视图架构要成为一个真正的框架需要什么?在我看来,这需要 GUI 控件与文档之间的自动耦合。程序员只需要指定数据生命周期、表示数据的控件以及包含数据的文档(或文档)。然后,框架将处理所有持久化问题,文档中数据的表示与视图中数据的表示之间的数据转换,并且所有这些都无需编写任何代码。在这个层面的文档-视图实现封装了数据转换,提供了一种将数据与文档耦合的架构,并通过实现和隐藏 GUI 控件与文档之间的耦合来强制执行一种方法论。
世界的其他地方怎么说?
“一个框架是一组程序员可以使用、扩展或自定义以满足特定计算解决方案的常见且预制的软件构建块。有了框架,开发人员在编写应用程序时不必从头开始。框架由对象集合构成,因此框架的设计和代码都可以重用。- *JavaFramework*。
这听起来像是一个架构和一组封装。好吧,2/3。
“一个应用程序的骨架,开发人员可以在其中插入自己的代码,并提供大部分通用功能。—— E. Gamma 等人,《设计模式》,Addison-Wesley,1995
好吧,这现在是一个截然不同的定义,在我看来,它肯定包含了方法论的概念,即使只是因为“骨架”必须定义开发人员如何插入自己的代码以及他们如何与“骨架”提供的通用功能进行接口。这里隐含(但非必然)的还有代码如何相互通信。
“一组定义对象之间交互模型的类……—— *Moduleco*(当然,他们在附加定义中完全搞砸了)
好的,这属于方法论的范畴,因为它显然强制了对象之间的交互风格,但它忽略了封装和架构方面。
- 一个全面、集成的类库。
- 整个架构是重用单位。
- 定义应用程序架构的控制逻辑和类交互。
- 以牺牲一些灵活性为代价来减少“琐碎工作”。
嗯。好的,这涵盖了我们正在讨论的三件事——封装(集成类库)、架构(整个架构)和方法论(定义控制逻辑和类交互)。虽然,这可能不完全是作者的本意,特别是当我们看到他们对设计模式的看法时,但你永远不知道。
到目前为止,我们已经看到了:
- 构建块。
- 骨架。
- 交互模型。
- 以上所有(某种程度上)。
坦白说,我不确定是否真的有一个好的定义。但实际上,我最喜欢的是《设计模式》作者的定义:
“当你使用工具包时,你编写应用程序的主体,并调用你想要重用的代码。当你使用框架时,你重用主体的代码,并编写它调用的代码。”
“结果不仅能更快地构建应用程序,而且应用程序具有相似的结构。它们更容易维护,而且对用户来说似乎更一致。另一方面,你失去了一些创造自由,因为许多设计决策已经为你做出了。”
“如果应用程序很难设计,工具包更难,那么框架就最难了……框架设计的任何实质性更改都会大大降低其益处,因为框架对应用程序的主要贡献是它定义的架构。因此,设计一个尽可能灵活和可扩展的框架至关重要。”
这并没有正式阐明框架由封装、架构和方法论组成的概念,但它绝对都隐藏在字里行间。它为 MFC 和 .NET 大部分是什么提供了另一种定义——工具包,而不是框架。这就是另一个问题的答案——如果 MFC 和 .NET 不是框架,那它们是什么?
结论
通过思考什么是框架,我认为我已经对这个主题作为一个入门话题进行了相当好的分析。也许我所谈论的东西与框架不同,但在我看来,这就是框架应该是什么以及它应该做什么。关于什么是框架,有很多不同的观点,所以现在你也有我的了。