研究建筑和设计

软件的体系结构和设计在很长一段时间以来一直抵制坚定的定义,因为作为一门学科的软件开发尚未完全掌握其所有复杂性和含义。 但是要创建有关这些主题的合理论述,您必须从某个地方开始。 本系列文章涉及进化体系结构和紧急设计,因此从一些定义,考虑因素和其他基础出发开始本系列是有意义的。

定义架构

软件体系结构是开发人员努力解决的最受关注但尚未被最了解的概念之一。 在会议上,关于建筑的座谈会和羽毛鸟聚会挤满了这座房子,但我们对其的定义仍然模糊。 在讨论架构时,我们实际上是在谈论几个不同但相关的问题,这些问题通常属于应用程序架构和企业架构的大类。

应用架构

应用程序体系结构描述了组成应用程序的粗粒度部分。 例如,在Java世界中,应用程序体系结构描述了两件事:用于构建特定应用程序的框架的组合(我称为框架级体系结构) ,以及更传统的逻辑关注点分离,我一直坚持应用程序架构的绰号。 拆分框架体系结构很重要,因为大多数面向对象语言的从业者都发现单个类不能很好地用作重用机制。 (您上次从Internet下载单个类以在项目中使用的时间是什么时候?)现代面向对象语言的重用单位是库或框架。 当您使用Java之类的框架丰富的语言启动新项目时,首先要关注的体系结构之一就是应用程序的框架级体系结构。 这种重用样式在Java世界中非常流行,以至于我开始说我们应该停止将Java编程称为一种面向对象的语言,而应该将其称为一种面向框架的语言。 在许多方面,框架级体系结构代表一种物理体系结构,由特定的构建块描述。

应用程序体系结构的另一个有趣方面描述了应用程序的逻辑部分如何组合在一起。 这是设计模式和其他结构描述的领域,因此往往更加抽象和逻辑化,而不是物理上的。 例如,您可以说Web应用程序遵循Model-View-Presenter模式,而没有指定使用哪种框架来实现这种逻辑排列。 当您开始处理应用程序的新部分时,这种逻辑安排是最有可能装饰工作空间白板的一种安排。

企业架构

企业体系结构与整个企业(通常意味着大型组织中运行的应用程序)如何使用应用程序有关。 为企业和应用程序体系结构之间的关系,一个常见的有用的隐喻企业比作城市规划和应用构建的架构。 城市规划人员必须考虑获取水,电,污水和其他服务,以使城市运转。 一栋建筑物消耗的水量不能超过其供水量的一部分。 企业体系结构为应用程序考虑了同样的事情:您不能允许一个应用程序占用网络的所有带宽,并且如果基础结构服务崩溃,则会(虚拟)浪费污水。

在过去的几年中,由于面向服务的体系结构(SOA),企业体系结构引起了很多关注。 SOA本身就是一个巨大的话题,因此本系列的后续文章将作为一个特例解决它。 它具有自己有趣的方面,因为当它指示应用程序构建的特征时,它模糊了企业和应用程序体系结构之间的界限。

前面的段落提供了这些重要概念的表面定义,但它们为其他更有趣,更细微的体系结构定义(包括从其他概念中收集的一些定义)打下了基础。

现有定义

许多聪明的人都在定义软件架构方面大胆尝试,因此我将让他们提供一些思考的方法。 在他的经典白皮书“谁需要建筑师?”中 (请参阅参考资料 ),Martin Fowler讨论了几个定义。 他引用了极端编程邮件列表中的帖子中的第一个:

“基于IEEE定义的RUP将架构定义为系统在其环境中的最高级别概念。软件系统的架构(在给定的时间点)是其通过接口交互的重要组件的组织或结构。 ,这些组件由依次较小的组件和接口组成。”“

正如我上面所描述的,此定义非常适合应用程序体系结构领域。 虽然含糊不清,但它确实抓住了架构责任的精髓:最高层次的概念。

福勒随后引用拉尔夫·约翰逊(Ralph Johnson),他在邮件列表的回复中对上述定义提出了异议:

“一个更好的定义是:'在大多数成功的软件项目中,从事该项目的专家开发人员对设计系统设计有共同的理解。这种共同的理解称为“体系结构”。这种理解包括如何将系统划分为各个组成部分。以及组件如何通过接口进行交互。”“

约翰逊(Johnson)提出了一个很好的观点,即软件开发不仅依赖于通信,还依赖于技术,而体系结构实际上代表的是关于系统的共享知识,而不是任何专门针对语言,框架和其他技术的信息。

在上述论文中,Fowler本人提供了我最喜欢的体系结构定义之一:

“架构与重要的东西有关。无论如何。”

这是适当的模糊性,但同时具有描述性。 关于架构和设计的许多争论都围绕对重要内容的误解。 对于业务分析师而言,重要的事情与对企业架构师而言重要的事情有所不同。 此定义很好地封装了您必须先在环境中定义术语,然后才能尝试定义其他内容。

Fowler的定义还突出了另一个重要方面,即定义与建筑一样细微的事物。 “重要内容”不仅因个人和群体而异; 这些差异实际上可以互斥。 例如,几乎所有SOA都会在灵活性和速度之间进行权衡。 您现在使用的旧客户端/服务器系统几乎可以肯定比替换它的基于Web,Portlet引擎,基于服务的版本更快。 除非新的应用程序是由糟糕的开发人员编写的,否则提供灵活性的额外层意味着用户的响应时间会增加,从而使他们的响应速度变慢。 也许架构师会告诉用户:“哦,顺便说一下,我们正在安装的新SOA东西对我们来说将变得更好,但是您的工作现在会更加麻烦。抱歉。” 也许这就是为什么建筑师比开发人员获得更高报酬的原因。

这留下了我最喜欢的架构定义:

“后来很难改变的东西。”

这个定义最适合进化架构的概念。 演化体系结构背后的核心思想之一是尽可能推迟决策,这使您可以替代最近经验证明是更好的选择。 这种风格的建筑的许多构建基块贯穿本系列文章,并激发了本系列的创作。

在离开体系结构的讨论之前,如果不讨论“建筑师”职称,我将不知所措。 令我们作为一个行业的头衔定义不清的人力资源部门感到恼火。 许多组织都希望提拔他们最好的开发人员,即那些对以后难以更改的事物做出重要决策的开发人员,但是除了“架构师”,没有好的行业术语。 也没有通用的职位描述,因此每个公司都定义了该职位的含义。 在第二部Matrix电影(Fowler分类为Architectus Reloadus )的结尾处,有些建筑师与Architect类似。 这些架构师上一次编写代码大约是在十年前,现在他们正在为您的公司做出重要决策。 他们使用的唯一软件开发工具是Visio。

另一种架构师角色是Fowler称为Architectus Oryzus (以我的同事之一David Rice命名)。 这些架构师在最困难的部分与其他开发人员一起为该项目积极贡献代码。 他们的职责还包括与其他项目涉众进行交互,以确保每个人都说相同的语言,使用相同的定义并理解他们需要理解的系统部分。 显然,这一积极角色对于实现进化架构的目标至关重要,因此将在本系列中反复出现。

定义设计

大多数开发人员已经具有相当不错的设计意识,因此我不会花太多时间来定义它。 它代表了软件如何协同工作的基本要素。 它涵盖了很多领域,例如设计模式,重构,框架和其他日常开发人员关注的领域。 设计大致介于BDUF(前期大设计)和Cowboy Hacking之间,如图1所示:

图1.设计范围
设计风格范围

图1左侧的频谱图表明,您可以预期开发软件时出现的所有成千上万的关注点,并尝试限制对它们的响应。 在后续的分期付款中,您将学到更多。 因为我没有花太多时间来定义设计,但这并不意味着我不会花很多时间谈论它。 本系列的大部分内容涵盖了如何允许设计随开发而出现,而不是一成不变地在编写第一行代码之前就付诸实践。

建筑和设计问题

有了当前对建筑和设计的可行定义,我想深入研究几个令人关注的领域。 所有这些主题都在基本层次上与体系结构和设计相交,因此在前面进行介绍可以让我在本系列的后面再回顾它们。 首先,我讨论技术债务,然后讨论复杂性,最后讨论普遍性。

本金和利息

每个开发人员都会意识到技术债务的概念,因此您会出于某种外部因素而在设计中做出折衷,例如进度压力。 技术债务类似于信用卡债务:您目前没有足够的资金,因此您会为将来而借款。 同样,您的项目没有足够的时间来做正确的事情,因此您破解了即时解决方案,并希望利用将来的时间来重新改造它。 不幸的是,许多经理似乎并不了解技术债务,从而导致人们对重新访问过去的工作产生抵触。

构建软件不像挖沟。 如果您在挖沟时做出让步,您只会得到宽度不均匀或深度不相等的情况。 今天有缺陷的沟渠并不能阻止您明天挖一个好的沟渠。 但是,您今天构建的软件是明天构建的基础。 为方便起见,现在做出的妥协导致熵在您的软件中累积。 在The Pragmatic Programmer一书中,Andy Hunt和Dave Thomas讨论了软件中的熵以及它为什么具有如此有害的作用(请参阅参考资料 )。 熵是衡量复杂性的一种方法,如果由于及时解决问题而现在添加复杂性,则必须在项目的剩余生命期内为此付出一定的代价。

假设您要向现有的,长期运行的项目添加新功能。 这些新功能对其具有一定的固有复杂性。 但是,如果您已经承担了技术债务,则必须解决系统中那些受到威胁的部分,以添加新功能。 因此,增加的成本反映了财务隐喻。 图2显示了在干净设计的系统(例如,技术债务很少或没有技术债务)中添加新功能所需的工作量与包含大量技术债务的典型系统之间的差异。

图2.技术债务和利息
努力添加新功能

您可以将固有的复杂性视为主要原则,而将以前方便的快捷方式所付出的额外努力视为兴趣所在。 复杂性本身就是一个有趣的话题。

基本与偶然的复杂性

我们在软件中解决的问题具有内在的复杂性,我称之为本质复杂性。 由折衷产生的复杂性使我们承担技术债务的方式有所不同。 它由外部施加的所有方法组成,这些方法使软件变得复杂,并且它不应该存在于完美的世界中。 我称这种偶然的复杂性。 我在我的著作《高效程序员》中定义并深入讨论了这些术语(请参阅参考资料 )。 这些术语通常不是纯粹的切割和干燥:它们存在于设计之类的频谱中。 一些示例将有助于阐明区别。

我的一位同事为一家工会公司开发了工资系统。 工会为其某些成员争取到的让步之一是在狩猎季节开始前有额外的休息日。 (嘿,他们必须有好的谈判代表。)所讨论的员工仅在一家工厂工作,但是适应加班假是整个公司薪资系统的合法组成部分。 更改为软件增加了很多复杂性,但这是必不可少的复杂性,因为它是要解决的业务问题的一部分。

总是再出现另一个例子:报名表上的字段级安全性。 许多商人认为他们希望对每个领域的安全特征进行细粒度的控制。 实际上,他们几乎总是讨厌它的实现,因为它给需要定义和维护所有元数据的用户带来了沉重负担。 我们其中一个项目的业务人员确实想要此功能,因此我们在其中一个屏幕上实现了部分功能。 一旦他们亲眼看到了使它工作所需的工作量,他们就决定,因为对应用程序的唯一访问是从一个上锁的办公室进行的,所以他们可以采用更粗粒度的安全性。 这是设计决策的一个很好的例子,一旦企业看到他们认为想要的东西的事实,就会出现设计决策。

走向意外复杂性的最末端是纯粹的管道练习,例如Enterprise JavaBeans(EJB)技术的前两个版本和BizTalk这样的工具。 一些项目需要这些工具带来的额外开销,但是它们除了增加使用它们的大多数项目的复杂性之外什么也不做。

三件事倾向于产生意外的复杂性。 我已经讨论了第一个:由于日程安排或其他外部压力而导致的即时黑客攻击。 第二个是重复 ,即实用程序员所说的违反DRY(请勿重复自己)原则的行为。 复制是软件开发中最隐蔽的一种力量,因为复制设法在没有开发人员意识到的情况下爬入了很多地方。 明显的例子是复制粘贴代码,但是更复杂的例子比比皆是。 例如,几乎每个使用对象关系映射工具的项目(例如Hibernate或iBatis)都有很多重复项。 您的数据库架构,XML映射文件和后备POJO的信息略有不同,但重叠。 可以通过为该信息创建规范来源并生成其他部分来解决此问题。 复制会伤害项目,因为复制会阻止进行结构更改或重构为更好的代码的尝试。 如果您知道需要在三个不同的地方进行更改,那么即使从长远来看它会使代码变得更好,您也避免这样做。

意外复杂性的第三个推动因素是不可逆性 。 您做出的任何无法逆转的决定最终都会导致某种程度的意外复杂性。 不可逆性会影响体系结构和设计,尽管它的影响在体系结构级别上更加普遍并且更具破坏性。 尽量避免做出不可能的决定或笨拙的决定。 我听到一些同事使用的口头禅是等到最后一个负责任的时刻 。 这并不意味着您应该推迟决策太长时间,而应该推迟足够长的时间。 您可以就某个架构问题做出决定的最后负责时刻是什么? 您可以避免决策的时间越长,您就越有机会自己做。 问问自己:“我现在需要做这个决定吗?” 和“我该怎么做才能让我推迟这一决定?” 如果您仅对决策过程应用一些独创性,您会对可以推迟到以后的事情感到惊讶。

我之前在框架级别的体系结构和应用程序体系结构之间做出的区分与“最后负责的时刻”的原则联系在一起。 应用程序体系结构倾向于是逻辑体系结构。 例如,假设您知道要分离Model-View-Presenter的关注点。 通常,您会通过选择满足某些或全部要求的框架而跳到该逻辑体系结构的物​​理实现。 查看是否可以推迟该决策,因为一旦物理实施就位,它就会约束您必须考虑的其他决策。 只要有可能,您就可以推迟框架决策的时间,让您有更多机会受到现实的污染,从而获得更好的选择。

猖amp的通用性

关于体系结构和设计的最重要的问题是我编造的一个词,叫做泛滥泛滥 。 在Java世界中,我们似乎有一种病:尝试使解决方案尽可能通用,从而过度设计解决方案。 这样做的动机很明确:如果我们在扩展中构建许多层,则以后可以更轻松地在其上构建更多的层。 但是,这是一个危险的陷阱。 由于通用性会增加熵,因此会破坏您在项目早期以有趣的方式发展设计的能力。 增加太多的灵活性会使对代码库的每次更改都更加复杂。

当然,您不能忽略可扩展性。 敏捷运动用一个伟大的短语概括了实现功能的决策过程:YAGNI(您将不需要它)。 这是试图避免过度设计简单功能的口头禅。 只需完全实现您现在需要的功能即可,如果以后需要更多功能,则可以添加它。 我已经看到一些Java项目因在通用性和可扩展性的祭坛上对架构和设计的妥协而blo肿,以至于这些项目失败了。 这当然具有讽刺意味,因为为该项目计划尽可能长的生存期会缩短其寿命。 学习如何在可扩展性和过度工程之间找到一条很好的界限是很困难的,这是我会经常回到的话题。

路线图

本文包含了许多挥舞的手,并且没有源代码,这使其与本系列其他所有即将发表的文章有所不同。 讨论诸如建筑和设计等复杂主题时固有的问题之一是必须进行上下文设置,以确保每个人都在同一页面上。 我为该系列的其余部分奠定了基础,在其中我将深入探讨与演化架构和紧急设计有关的特定领域。 每篇文章都将深入探讨这两个概念中一个或两个的特定说明性方面,并附带大量详细信息和源代码。 接下来 :我谈论通过测试驱动的开发进行紧急设计,我将其重命名为测试驱动的设计 。


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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值