将潜在的整体应用程序拆分为几个较小的应用程序是否有助于防止错误?[关闭]


48

另一种询问方式是:为什么程序趋于单一?

我想到的是像Maya这样的动画包,人们将其用于各种不同的工作流程。

如果将动画和建模功能拆分成自己的独立应用程序并分别进行开发,并在它们之间传递文件,那么它们是否会更易于维护?


9
If the animation and modelling capabilities were split into their own separate application and developed separately, with files being passed between them, would they not be easier to maintain?混合使用时不要轻易扩展而维护模块则更容易-就其本身而言,并非没有复杂性或可疑的设计。Maya可能是维护的地狱,而其插件却没有。或相反亦然。
莱夫

37
我要补充一点,一个单一的程序往往更容易销售,而且对大多数人来说更容易使用
DarthFennec

2
@DarthFennec最好的应用程序对用户来说就像一个应用程序,但是可以利用引擎盖下必要的一切。您访问的各种网站有多少种微服务?他们几乎都不再是巨石了!
corsiKa

23
@corsiKa通过将桌面应用程序编写为在后台进行通信的多个程序,通常没有任何好处,而仅仅编写多个模块/库并将它们链接在一起成为一个整体的二进制文件则无法获得任何收益。微服务可完全满足不同目的,因为它们允许单个应用程序在多个物理服务器上运行,从而使性能随负载而扩展。
DarthFennec

5
@corsiKa-我猜想我使用的绝大多数网站还是庞大的。毕竟,大多数Internet都在Wordpress上运行。
DavorŽdralo

Answers:


94

是。通常,两个较小,较不复杂的应用程序比单个较大的应用程序更易于维护。

但是,当应用程序全部协同工作以实现目标时,您会收到一种新型错误。为了使他们能够一起工作,他们必须交换消息,并且即使每个应用程序都可以正常运行,这种编排也会以各种方式出错。拥有一百万个微型应用程序有其自身的特殊问题。

当您向单个应用程序中添加越来越多的功能时,单片应用程序实际上是最终的默认选项。当您单独考虑每个功能时,这是最简单的方法。只有当它变大时,您才可以查看整体并说“您知道什么,如果我们将X和Y分开,效果会更好”。


6
是的,还有性能方面的考虑因素,例如传递指针与序列化数据的成本。
JimmyJames

65
“通常来说,与单个大型应用程序相比,维护两个较小的较简单应用程序要容易得多。” -是的,除非不是。在很大程度上取决于这两个应用程序之间的交互位置和方式。
布朗

10
“通常,与一个大型应用程序相比,维护两个较小的,较不复杂的应用程序要容易得多。” 我想对此需要更多解释。为什么从代码库中生成两个而不是一个可执行文件的过程会神奇地使代码更容易?决定代码推理的难易程度,耦合程度以及类似情况的原因。但这是逻辑上的分离,与物理上的分离无关。
Voo

11
@Ew物理分隔不会强制逻辑分隔,这就是问题所在。我可以轻松地设计一个将两个独立的应用程序紧密结合在一起的系统。当然,这里涉及到一些相关性,因为花费时间来分离应用程序的人员很可能足以胜任这些事情,但是没有理由假设任何因果关系。按照同样的逻辑,我可以断言使用最新的C#版本会使代码的维护更加容易,因为那种与时俱进的团队可能也会担心代码的维护。
Voo

9
我认为这里的讨论可以用2条语句来概括:1)拆分应用程序本身并不能使应用程序更易于维护-相反,它提供了另一个可能的失败点2)拆分应用程序迫使您考虑在何处拆分与从未完成的整体相比,它提供了一个优势。
R. Schmitz

51

将潜在的整体应用程序拆分为几个较小的应用程序是否有助于防止错误

实际上,事情很少如此简单。

首先,拆分绝对无助于防止这些错误。有时可以帮助更快地发现错误。由孤立的小组件组成的应用程序可以允许对那些组件进行更多的单独(种类为“单元”-)测试,这有时可以使发现某些错误的根本原因更加容易,从而可以更快地修复它们。

然而,

  • 即使从外部看似整体的应用程序可能在内部也包含许多可单元测试的组件,所以对于整体式应用程序,单元测试不一定会比较困难

  • 正如Ewan已经提到的那样,几个组件的交互会带来额外的风险和错误。而且,调试具有复杂进程间通信的应用程序系统比调试单进程应用程序要困难得多。

这在很大程度上还取决于较大的应用程序可以拆分成多个组件的程度,组件之间的接口有多广泛以及这些接口的使用方式。

简而言之,这通常是一个权衡,在“是”或“否”答案通常是正确的情况下,没有什么是正确的。

为什么程序趋于单一

有吗 环顾四周,世界上有成千上万的Web应用程序对我来说看起来并不十分单一,相反。还有许多提供插件模型的程序(AFAIK,甚至您提到的Maya软件都可以)。

他们会不会更容易维护

这里的“更容易维护”通常来自于这样一个事实,即不同的团队可以更轻松地开发应用程序的不同部分,从而可以更好地分配工作负载,专注于更专业的团队等等。


4
在最后一句话中,Conway的定律说系统结构倾向于模仿org。结构:开发人员/团队比其他部分更熟悉某些部分,因此尽管应该在最相关的部分进行修复/改进,但开发人员将其分为“他们的”部分可能比(a)了解如何更容易其他部分起作用,或(b)与对该部分更熟悉的人一起工作。这与@TKK提到的“接缝”有关,以及查找和执行“正确” /简单的接缝有多困难。
Warbo

38

在这一点上,我将不同意大多数。将应用程序分为两个单独的应用程序本身并不会使代码更易于维护或推理。

将代码分成两个可执行文件只会改变代码的物理结构,但这并不重要。决定应用程序有多复杂的因素是组成应用程序的不同部分之间的紧密耦合。这不是物理属性,而是逻辑上的属性。

您可以拥有一个完整的应用程序,该应用程序将不同的关注点和简单的界面清晰地分开。您可以拥有一个微服务架构,该架构依赖于其他微服务的实现细节,并且与所有其他微服务紧密结合。

的确,在尝试为每个部分建立清晰的接口和需求时,如何将一个大型应用程序拆分为多个较小的应用程序的过程非常有用。用DDD讲,这将取决于您的有限上下文。但是,是创建大量微型应用程序,还是创建具有相同逻辑结构的大型应用程序,更多的是技术决定。


但是,如果有人采用具有多种编辑模式的桌面应用程序,而只是为每种模式创建一个桌面应用程序,则用户将分别打开而不是具有接口。难道不消除专用于产生“用户可以在编辑模式之间切换”的“功能”的大量代码吗?
大鸭

3
@TheGreatDuck听起来好像还会消除大量不愿意在不同应用程序之间切换的用户。;)但是,是的,消除功能通常会导致代码更简单。消除了拼写检查,您将消除出现拼写检查错误的可能性。只是很少这样做,因为有人想要它而添加了该功能。
Odalrick

1
@TheGreatDuck当然,UX的设计应该先于任何体系结构决定。如果没有人使用您的程序,那么拥有最佳设计的体系结构是没有意义的。首先,根据技术细节决定要构建的内容。如果需要两个单独的应用程序,那就去吧。您仍然可以通过共享库共享很多代码。
Voo

系统的复杂性是由于零件的紧密耦合是真的吗?我想说的是,如果在引入间接通信和通信时对系统进行分区,则总的复杂度会增加,尽管特定单个组件的复杂性被隔离在更为有限的复杂性的有限状态下。
Alex

1
@TheGreatDuck这里的基本假设是,系统具有某些共同点,实际上必须以一种或另一种方式相互通信。我不认为OP会问是否出于某种奇怪的原因而将两个完全不同的应用程序捆绑在一起是否可以更容易维护(如果分开)。看起来像一个奇怪的边缘案例,在实践中很少出现(尽管我确定有人在这样做)。
Voo

15

完成拆分后,维护起来更容易,是的。但是拆分它们并不总是那么容易。试图将一个程序的一部分拆分成一个可重用的库,这揭示了原始开发人员在哪里没有考虑接缝的位置。如果应用程序的一部分正在深入应用程序的另一部分,则可能很难修复。撕开接缝会迫使您更加清晰地定义内部API,最终使代码库更易于维护。可重用性和可维护性都是定义明确的接缝的产物。


很棒的帖子。我认为您所谈论的经典/规范示例是GUI应用程序。很多时候,GUI应用程序是一个程序,后端/前端紧密耦合。随着时间的流逝出现问题……就像其他人需要使用后端一样,但是不能,因为它与前端绑定在一起。否则后端处理时间太长,使前端陷入困境。通常,一个大的GUI应用程序分为两个程序:一个是前端GUI,一个是后端。
Trevor Boyd Smith

13

重要的是要记住,相关性不是因果关系。

构建一个大的整体,然后将其拆分为几个小部分,可能会也可能不会导致良好的设计。(它可以改善设计,但不能保证如此。)

但是,良好的设计通常会导致将系统构建为几个小零件,而不是一个大型整体。(整体设计可能是最好的设计,可能性要小得多。)

为什么小零件更好?因为他们更容易推理。而且,如果很容易就正确性进行推理,那么您更有可能获得正确的结果。

引用CAR Hoare:

构建软件设计的方法有两种:一种方法是使它变得如此简单,以至于显然没有缺陷,另一种方法是使它变得如此复杂以至于没有明显的缺陷。

如果是这样,为什么有人会构建不必要的复杂或整体解决方案?Hoare在接下来的句子中提供了答案:

第一种方法要困难得多。

后来又在同一来源(1980年图灵奖演讲)中:

可靠性的代价是追求最大程度的简单性。这是非常富有的人最难支付的价格。


6

这不是一个回答是或不是的问题。问题不仅在于维护的简便性,还在于有效使用技能的问题。

通常,编写良好的整体应用程序是有效的。进程间和设备间的通信并不便宜。分解单个过程会降低效率。但是,在单个处理器上执行所有操作可能会使处理器超负荷并降低性能。这是基本的可伸缩性问题。当网络进入画面时,问题变得更加复杂。

编写良好的整体应用程序可以在单个服务器上作为单个进程高效运行,可以轻松维护并避免出现缺陷,但仍不能有效地利用编码和体系结构技能。第一步是将流程分解为仍按相同流程执行但遵循内聚和松散耦合原则独立编码的库。在此级别上做好工作可以提高可维护性,而很少影响性能。

下一步是将整料分成单独的过程。这比较困难,因为您进入了棘手的领域。引入竞争条件错误很容易。通信开销增加了,您必须注意“聊天界面”。回报是巨大的,因为您打破了可伸缩性的障碍,但缺陷的可能性也在增加。多进程应用程序更易于在模块级别进行维护,但是整个系统更加复杂并且更难于进行故障排除。修复可能非常复杂。

当将流程分发到单独的服务器或云风格的实现中时,问题会变得更加棘手,收益也会更高。可扩展性猛增。(如果您考虑的是无法实现可伸缩性的云实施,请认真考虑。)但是,在此阶段出现的问题难以识别和深思熟虑。


4

没有。它并没有使其更容易维护。如果有什么欢迎更多的问题。

为什么?

  • 这些程序不是正交的,它们需要在合理范围内保存彼此的工作,这意味着有一个共识。
  • 这两个程序的许多代码是相同的。您要维护一个公共共享库,还是维护两个单独的副本?
  • 您现在有两个开发团队。他们如何沟通?
  • 现在,您有两种需要的产品:

    • 通用的UI样式,交互机制等。因此,您现在遇到了设计问题。(开发团队又如何沟通?)
    • 向后兼容(可以将Modeller v1导入到动画师v3中吗?)
    • 现在必须跨两倍数量的产品更新云/网络集成(如果具有功能)。
  • 您现在拥有三个消费市场:建模者,动画师和建模者动画师

    • 他们将有相互冲突的优先事项
    • 他们将有相互矛盾的支持需求
    • 他们的使用方式会有冲突
  • 建模者动画师是否必须打开两个单独的应用程序才能在同一文件上工作?是否存在同时具有这两种功能的第三个应用程序,一个应用程序是否加载了另一个应用程序的功能?
  • 等等...

话虽这么说,较小的代码库也同样易于在应用程序级别进行维护,但是您将无法获得免费的午餐。这是Micro-Service / Any-Modular-Architecture的核心问题。它不是万能药,在应用程序级别的维护困难被换成了业务流程级别的维护困难。这些问题仍然是问题,它们不再存在于代码库中,将需要避免或解决。

如果在业务流程级别解决问题比较简单,那么在每个应用程序级别上解决问题,则有必要将其拆分为两个代码库并处理业务流程问题。

否则,只是不这样做,可以通过改善应用程序本身的内部模块化来更好地为您服务。将代码段推入内聚的位置,并更易于维护该应用程序充当其插件的库。毕竟,整体就是图书馆景观的编排层。


3

有很多好的答案,但是由于几乎要死了,所以我也会把帽子也戴上。

根据我作为软件工程师的经验,我发现这不是一个简单的问题。它实际上取决于应用程序的大小规模目的。依靠更改惯性所需的较旧应用程序通常是整体的,因为这是很长一段时间以来的普遍做法(Maya属于此类)。我认为您通常是在谈论较新的应用程序。

在或多或少是单个关注点的足够小的应用中,维护许多分离的零件所需的开销通常超过了进行分离的用途。如果可以由一个人维护,则可以将其制成一体而不会引起太多问题。该规则的例外是,当您具有方便地(逻辑上)分开的许多不同部分(前端,后端,也许是中间的一些数据层)时。

在我的经验中,即使是非常关注的应用程序,将其拆分也很有意义。您可以减少所有可能的错误类别,以换取其他(有时更容易解决)错误。通常,您还可以让一些人孤立地工作,从而提高生产力。但是,如今,许多应用程序被很好地拆分,有时会损害自身。我也曾在一些团队中工作,他们将应用程序不必要地分成了许多微服务,以至于当事情停止相互交流时,这会带来很多开销。此外,在每次连续拆分时,必须掌握有关每个部分如何与其他部分交谈的所有知识会变得更加困难。两者之间是平衡的,您可以通过此处的答案看出来的方法并不十分清楚,


2
我作为程序员的第一份工作是作为一个千年虫程序员。我正在使用的软件被分解为数百个小程序,它们全部完成了一部分工作,与批处理文件捆绑在一起,并使用文件来传达状态。这是一个大混乱,它是在计算机速度缓慢,内存不足且存储昂贵的时代发明的。当我使用它时,代码已经有10-15年的历史了。一旦完成,他们就会询问我的建议,而我的建议是将所有内容都转换为新的整体式应用程序。他们做到了,一年后,我非常感谢您。
Pieter B

@PieterB我也有类似的经历。不幸的是,从很多方面来看,“尖端”技术都是非常庞大的货运活动。许多公司没有选择最佳的工作方法,而是会毫无疑问地遵循FAANG当时所做的一切。
CL40

并且:一旦编译,可能会成为一个整体应用程序,这可能是一个非常模块化的应用程序,在代码方面是明智的。
Pieter B

1

对于UI应用程序,不太可能减少错误总数,但是会将错误混合的平衡转移到由通信引起的问题上。

说到面向用户的UI应用程序/站点-用户非常耐心,要求响应时间短。这会使任何通信延迟都变成错误。结果,由于单个组件的复杂性降低,非常困难的错误以及跨进程/跨机器通信的时序要求,人们将可以减少潜在的错误。

如果程序处理的数据单位很大(即映像),那么任何跨进程的延迟都将更长且更难消除-诸如“将转换应用于10mb映像”之类的操作将立即获得+ 20mb的磁盘/网络IO从内存格式到序列化格式再转换为2。实际上,您没有太多可以向用户隐藏这样做的时间。

此外,任何通信,尤其是磁盘IO都必须经过AntiVirus / Firewall检查-这不可避免地增加了另一层难以重现的错误以及更多的延迟。

在通信延迟不是很关键或已经不可避免的情况下,拆分整体式“程序”非常有用

  • 可并行化的信息批量处理,您可以在其中交换一些额外的延迟,以显着改善各个步骤(有时只需使用一次成品即可消除对自定义组件的需求)。较小的单步足迹可能使您使用多台便宜的机器,而不是一台昂贵的机器。
  • 将整体式服务拆分为耦合程度较低的微服务-并行调用多个服务而不是最不可能调用一个服务将不会增加额外的延迟(如果每个服务速度更快且没有依赖性,甚至可以减少总时间)
  • 移出用户期望花费很长时间的操作-渲染复杂的3D场景/电影,计算有关数据的复杂指标,...
  • 各种“自动完成”,“拼写检查”和其他可选帮助都可以(通常)是外部的-最明显的例子是浏览器的url自动建议,其中您的输入始终发送到外部服务(搜索引擎) 。

请注意,这适用于桌面应用程序和网站-程序的面向用户部分往往是“整体的”-与单个数据段相关的所有用户交互代码通常都在单个进程中运行(拆分是很正常的以每个数据为基础进行处理,例如HTML页面或图像,但与该问题正交。即使对于大多数具有用户输入的基本站点,您也会看到验证逻辑在客户端运行,即使使服务器端更加模块化并减少了复杂性/代码重复也是如此。


0

[是否]有助于防止错误?

防止?好吧,不,不是真的。

  • 它有助于检测错误
    也就是说,您甚至不知道自己拥有的所有错误,只有在您尝试将整个混乱分解成较小的部分时才发现。因此,从某种程度上讲,它阻止了这些错误在生产中出现-但是这些错误已经存在。
  • 它有助于减少错误的影响
    整体应用程序中的错误有可能导致整个系统崩溃,并使用户根本无法与您的应用程序进行交互。如果您将该应用程序拆分为多个组件,则大多数Bug(根据设计)将仅影响其中一个组件。
  • 它为新的错误创建了一个场景
    如果要保持用户体验不变,则需要为所有这些组件包括新的逻辑进行通信(通过REST服务,通过OS系统调用,您拥有什么),以便它们可以与用户的POV无缝交互。
    举一个简单的例子:您的整体应用程序使用户无需离开应用程序即可创建模型并对其进行动画处理。您将应用程序分为两个部分:建模和动画。现在,您的用户必须将建模应用程序的模型导出到文件中,然后找到该文件,然后使用动画应用程序将其打开...让我们面对现实,有些用户并不会那样,因此您必须为建模应用程序以导出文件自动启动动画应用程序使其打开文件。而且,这种新逻辑可能非常简单,但可能存在许多与数据序列化,文件访问和权限,用户更改应用程序安装路径等有关的错误。
  • 这是应用急需的重构的完美借口
    当您决定将单片应用程序拆分为较小的组件时,(有希望的)与最初设计时相比,它对系统有了更多的了解和经验,因此,您可以应用许多重构来编写代码更清洁,更简单,更高效,更有弹性,更安全。这种重构可以在某种程度上帮助防止错误。当然,您也可以对单片应用程序应用相同的重构,以防止出现相同的错误,但是您不能这样做,因为它是如此单片,以至于您害怕触摸UI中的某些内容并破坏业务逻辑¯\ _(ツ) _ /¯

因此,我不会说您只是通过将整体应用程序分解为较小的组件来预防错误,但实际上是使您更容易达到可以更轻松地预防错误的目的。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.