野外偷拍

    技术2024-04-07  88

    关于本系列

    本系列文章旨在为人们经常讨论但难以捉摸的软件体系结构和设计概念提供新的视角。 通过具体的示例,尼尔·福特为您提供了进化架构和紧急设计的敏捷实践的坚实基础。 通过将重要的架构和设计决策推迟到最后一个负责任的时刻,可以防止不必要的复杂性破坏您的软件项目。

    我在本系列中的许多示例都依赖于精益软件的概念,即将决策推迟到最后一个负责任的时刻 。 但是,您怎么知道那一刻何时到来? 您怎么知道在更好的道路出现之前您可以推动设计决策走多远? 什么样的项目从紧急设计中受益最大? 最终的Evolution Architecture和紧急设计部分将回答这些问题,然后回顾整个系列的一些要点。

    首先,我从唐纳德·拉姆斯菲尔德的诗歌开始。

    未知的未知数

    有已知的未知数。 就是说有些事情我们现在知道我们不知道。 但也有未知的未知数。 有些事情我们不知道,我们不知道。 - 美国前国防部长唐纳德·拉姆斯菲尔德

    拉姆斯菲尔德区分未知类别,从我们知道存在但尚未发现的未知开始。 但是他也承认事物的存在超出了我们的知识或经验,以至于我们甚至都不知道寻找它们:“未知的未知数”。

    拉姆斯菲尔德在谈论战争的不确定性,但他本来可以谈论软件。 如果您编写了任何非同寻常的软件,则可能涉及未知的未知数问题,这是软件设计中最大的问题之一。 您认为自己对实施解决方案时会遇到的问题有深刻的了解,但是不可避免地会出现意外问题。 例如,与开源框架进行交互并不像您想的那么简单,或者一个问题比最初看起来要细微得多,需要仔细得多的思考。

    意外的设计问题不断出现,因为软件的容错能力很低-远低于物理系统。 例如,考虑一下您周围的建筑物。 建造建筑物是一个多人,数月(或数年)的项目。 现在考虑在相似的时间范围内构建的多人软件项目。 这些项目的规模相似,但是物理建筑物对结构缺陷的容忍度更高。 如果电灯开关板不能完全覆盖电线容纳墙壁上的Kong,则建筑物不会倒下。 然而,软件中的小缺陷会使它崩溃。 与原子相比,位的故障容忍度是不可原谅的。 当然,软件(较软)也更容易修复:我们可以铲平漏洞(修复漏洞)并立即重新制造房屋。

    如果机翼从飞机上掉下来,法医工程师将首先关注机翼在飞机上的附着位置:在物理系统中,功能误差通常很高。 但是在软件中,执行一行代码通常会引起不稳定,这种不稳定情况在成百上千个看似无关的代码行中都不会显现出来。

    预先设计软件很困难,因为每个部分都以无数种有时是意想不到的方式彼此交互。 预测这些交互的影响超出了我们当前的能力。 新兴设计包含了令人惊讶的复杂性的必然性,并试图减轻变化的破坏性影响。

    设计范围

    紧急设计不是二进制状态。 您不能确切地说您的设计是100%敏捷或0%敏捷。 它存在于频谱上,如图1所示:

    图1.设计范围

    在图1的最左侧,您可以看到传统的Big Design Up Front(BDUF),它体现在许多常见的开发方法中。 BDUF以最好的稻草人形式体现了象牙塔建筑师创造的设计作品,将它们从地板上的Kong中掉下来,使不幸的编码人员无需更改即可实现。 在非模仿形式下,这种设计方法努力地尝试在编码开始之前发现所有有趣的事物。 它是设计软件的预测性,主动性模型。

    图1的右侧代表您在高中时执行的编码类型:对其进行破解,直到它起作用为止,然后继续进行修改。 这在小范围内效果很好(基本上,只要问题足够小以适合您的头部即可),但在此范围之外并没有太大作用。

    新兴的设计介于这两个极端之间,但更偏向右侧而不是左侧。 紧急设计是一种响应式,React式的设计软件方式。

    令人困惑的是,面对如此多的失败和未完成的项目,如此众多的组织继续使用BDUF。 我并不是在建议您不能成功使用BDUF。 (实际上,我过去曾做过许多这样的项目。)但是这种开发方法的记录很差,而且已经有几十年了。 弗雷德·布鲁克斯(Fred Brooks)的开创性著作《 神话人月 》( Mythical Man Month)讨论了这种风格的构建软件的问题,该书于1975年出版(请参阅参考资料 )。

    团队尝试这种开发风格也就不足为奇了,因为从某种意义上讲,它与设计在更传统的工程中的工作方式相匹配。 如果要制作小部件或iPod,则必须先进行所有设计,因为您无法重构原子。 考虑原始的英特尔奔腾处理器。 发行后,浮点数学单元中发现了一个错误,要求每个操作系统制造商编写特殊代码来解决该问题。 制造过程完成后,您将无法再更改硬件。 与软件截然相反:软件项目的大部分生命都发生在初始版本之后,即通过增强功能,错误修复和其他“维护”活动。 我们处理位而不是原子,并且位无限延展。

    预先需要多少设计?

    敏捷设计不会在项目或阶段开始时忽略设计。 当您最不了解问题的实质时,它会尽力在过程的早期尽可能少地做。 经常有人问我:“在项目开始时,您如何确定多少设计合适?” 不同类型的项目具有不同的标准,并且它们的类型和复杂性比大多数非技术人员意识到的要多。 基本上,您正在尝试权衡两件事:前期设计在您的早期决策足够准确以免将来发生更改时起作用,而后期更改的成本则足以使收益至关重要。 因此,您需要在早期阶段就具备丰富的知识,并结合开发技术,这些技术会使后续更改变得昂贵。 敏捷设计反对这一概念,因为人们往往过高估计了他们尽早做出准确决策的能力,而后来又遭受了不断扩大的后果。

    以下是一些项目示例,您需要为它们做更多的设计:

    那些具有极其稳定的要求,并且预计不会在几年左右变化的产品。 出于安全原因,适用于高度隔离的环境(例如,太空旅行,水下探险)。 您之前为同一项目编写了完全相同的软件,并且具有相同的人员,并且范围没有变化。 (您将对此进行致命的准确估算。) 针对高度受限的环境(例如嵌入式系统)的项目,以确保您考虑了该环境的约束。 (我仍将尝试允许行为功能尽可能多地出现。)

    您不应该对其进行过多前期设计的项目类型包括:

    与大多数业务应用程序一样,项目的需求变化很大,并且需求变化很大(数月或数周)。 那些需要对外部因素(例如市场条件)做出React的企业。 您不确定许多技术或业务细节的项目。 请注意,这实际上涵盖了每个项目,回溯到拉姆斯菲尔德的“未知未知数”。 受益于开发的项目而不是人为地区分“完成”的项目。 从来没有完成过任何软件项目,因此您总是真正在购买订阅,并且您越早意识到它,您就越会喜欢它。

    选择正确的时间做出决定虽然困难,但却很重要。 所有项目都是唯一的,因此特定的建议毫无用处。 但是,存在一些常规准则:

    请注意,当起作用的“最简单的事物”工作了一段时间之后,它要解决的问题的重要性或复杂性有了很大的提高。 例如,假设您一直使用数据库作为基本后台异步行为的简单消息队列。 但是现在,在性能开始受到影响的同时,一些新的要求将增加各种异步行为。 现在是重新考虑“最简单的方法”决策的最佳时机,因为您的解决方案不再符合该标准。 当着眼于问题的整体范围时,请尝试隔离那些容易出现紧急设计技术的地方。 例如,假设您正在开发需要地理编码支持的应用程序,而您之前从未使用过地理编码库。 您应该执行一些加急操作 (简单,定向的研发项目),以确保您对估计有足够的了解。 尝试防止基于不完全的了解而做出任何体系结构(以后很难更改)的决策,并重新访问应用程序其余部分与本孤立部分之间的接口点,以查看是否可以根据更好的理解进行改进。 尝试使应用程序之间的交互点保持流畅。 诸如简单对象访问协议(SOAP)之类的协议的不良影响之一是它们对刚性结构和强类型的坚持。 灵活性的秘诀在于降低特异性,而不是提高特异性,而实现的灵活性正引起人们对代表性状态转移(REST)和相关技术的极大兴趣。 构建API时,请尝试接受最合理的最通用种类,这些种类也适用于集成。

    加起来

    我将在系列末尾总结前18期的主要主题。

    编码为设计

    在“ 代码与设计之间的关系 ”一文中,我引用了Jack Reeves的一篇文章,该文章建议软件设计包括项目的整个源代码,而不仅仅是我们通常认为的设计工件(白板图,统一建模语言等)。 他将源代码与工程师制定的计划进行比较,这些计划指定了制造团队将设计转换为原子所需的一切。 我们的计划是源代码,编译器会将其转换为位。 如果代码是设计的,那么我们使用的计算机语言和框架定义了我们可以设计的原始材料。

    功能更强大的语言提供的杠杆作用弥补了其高级功能的不足。 尽管因为已有足够的低成本开发人员来选择和标准化使用十多年的语言是很不错的选择,但是您还必须接受这样一个事实,即您不能像使用更现代的语言和工具的竞争对手那样快地前进。 许多组织过于注重标准化,却牺牲了创新和理智。 我已经看到多个项目被迫使用一套标准的框架,这些框架对于该问题是非常荒谬的,这对任何类型的设计发现都是有害的,因为它被偶然的噪音所遮盖。

    这并不意味着您应该允许每个开发人员选择他或她自己的工具堆栈。 这确实意味着您必须密切关注标准化所提供的实际价值,并考虑合理的替代方案。 例如,也许您想继续为项目使用Java技术,但是您可以开始使用更高级的工具来进行构建,测试和其他开发人员任务。 代码在软件项目中随处可见,无论有意还是无意,都体现了设计。

    拥抱不确定性

    许多方法试图避免不确定性。 如果要使用原子进行构建,不确定性将很糟糕,因为一旦将原子的形状强制改变为特定形状,更改它的配置将非常昂贵。 但是,当您使用位进行构建时,更改非常容易并且非常可取。 在软件中,避免更改既困难又不可取。

    敏捷方法论试图使用诸如单元测试,重构,持续集成和迭代开发之类的技术找到使变更变得更容易的方法。 新兴的设计体现了关于设计的敏捷哲学。 当做出设计决定时,请问自己:

    我现在需要做这个决定吗? 我可以安全地推迟这个决定吗? 我该怎么做才能使决定可逆?

    如果您所处的环境可以轻松地重构代码,那么暂时做出次优的决定就不会那么令人恐惧,因为您可以轻松地进行修复。 如果您设置项目以适应变化,则推迟决策不会造成损害,因为您已经针对课程更正进行了优化。 拥抱变化需要能够客观地看待决策,并改变使事情变得更糟的决策。

    收获惯用模式

    紧急设计的另一个方面是能够查看代码中已经存在的有用设计元素,并将它们作为惯用模式进行收集的能力,我在“ 利用可重用代码,第1部分 ”中将其定义为对过去问题的有效解决方案。 这些发现的图案是贵公司的头号珠宝,因为它们是设计元素,可以捕捉到有关公司开展业务的方式的有用信息。 编写的代码令人沮丧的是,它封装了独特的价值:其余的大部分只是将内容推入和拉入数据库,构建HTML等。 惯用模式比在联合应用程序设计(JAD)或象牙塔设计中编写的模式更有价值,因为它们已经满足了实用性的关键标准之一:它们已被用于解决实际问题。

    您可以通过多种方式来收获这些模式。 您可以创建一个API,它在机械上很简单,但并不引人注目-看起来就像您使用的所有其他API。 您还可以使用元编程和属性来捕获某些惯用模式类别(请参阅“ 利用可重用代码,第2部分 ”)。 在“ 使用DSL ”,“ Fluent接口” ,“ 在Groovy中构建DSL ”和“ 在JRuby中构建DSL ”中,我还讨论了如何使用特定领域语言(DSL)捕获域模式。

    始终尝试识别有用的设计元素,并避免做出人为地难以做的决定。 例如,将架构元素添加到应用程序中(例如,可扩展性的层或面向服务的体系结构中的服务)会使您的代码在将这些元素添加到代码库后立即变得更加复杂,而不是在您开始使用它们时 。 确保在正确的时间添加复杂性,因为这是偶然的复杂性,直到您开始使用它为止。

    结论

    本系列的目标之一是迫使我自己以不同的方式看待设计并记录这次旅行。 希望您喜欢坐在乘客座位上。 我还没有放弃我的逗留,只是改变了话题。 请继续关注我的下一个关于功能思维的developerWorks系列文章。


    翻译自: https://www.ibm.com/developerworks/java/library/j-eaed19/index.html

    相关资源:jdk-8u281-windows-x64.exe
    Processed: 0.012, SQL: 9