为什么Web架构应该松散耦合?

29

每当我看到 ASP.NET MVC 项目时,我总是看到松耦合架构。

在 Web 架构中,为什么我需要松耦合(如果我不进行单元测试)?

这样做的优点缺点是什么?

解耦层/类的主要原因是什么?

如果我不想改变我的数据访问层(DAL),例如何时应该更改整个 DAL?因此,我可以将我的 DAL 与 UI 耦合。这样做有什么问题吗?

13个回答

32

松散耦合(Loose Coupling)允许您在不影响其他部分的情况下对应用程序的某个区域进行更改。理论上,它允许您执行一些操作,例如更改数据访问层而无需重建业务或UI层。

松散耦合确实使您的应用程序更加灵活、更易于变更和更易于维护(因为您无需担心应用程序中一个区域的更改会破坏另一个区域)。


3
我想补充一点,“更容易维护”。如果从长远来看它们不容易维护,那么你做错了什么。 - Dan Esparza
同意。松耦合,紧凑内聚永远是正确的方式。这是我从软件工程课上学到的少数好东西之一。 - ubiquibacon
如果我不需要解耦层怎么办?什么时候改变整个DAL?我不关心理论上可能发生的事情。 - Rookian
你需要取得平衡。你可以将所有东西分离出来并进行依赖注入,但如果你不打算改变它们,为什么要这样做呢?通过将功能组合在一起,您可以提高应用程序中的重复使用性。你可能永远不想改变数据访问层(DAL),但如果你需要改变它的工作方式或添加新功能,你只需要在一个位置进行修改。 - Simon Halsey
只更改一个位置的事实仅适用于您不进行大型更改的情况,对吗? - Rookian

22

如果一个项目不是微不足道的小,我定义为代码行数少于几千行(具体取决于编程语言),这时使用松散耦合的设计可以节省很多时间。

因为一旦过了超小型项目,每次更改或更新都会变得更加困难,尤其是当紧密耦合时。而松散耦合使您可以继续前进,添加功能、修复错误等等。

在某一点上,我认为任何程序都会成为维护、更新和添加的噩梦。设计越松散耦合,这一点就能够推迟。如果过于紧密耦合,也许在约10,000行代码后就会变得难以维护,有些功能甚至需要从头开始重写。

松散耦合有助于让它发展到1,000,000 - 10,000,000行代码并仍能够在合理的时间内进行更改和添加新功能。

这些数字并不是要字面理解,只是为了给出何时使用松散耦合有帮助的感觉。

如果您永远不需要更新程序并且它相当简单,则可以将其设计为紧密耦合。甚至可以这样开始,但是必须知道何时分离代码,而且您仍然需要经验来编写松散耦合的代码以了解在何时使用它才有益。

Enterprise Fizzbuzz是一个刻意幽默的例子,说明过度工程化是可能的,并非每个项目都需要相同水平的解耦。

MVC通常被认为是一个不错的起点,因为大多数项目会变得足够大,以至于MVC设计能够帮助到开发。当项目变得更大时,这种程度的解耦将不足以应对,M部分需要进一步拆分为多个层次等等。并没有适用于所有情况的标准,但对于大多数项目来说,MVC提供的程度足够了。


12

在理论上,松耦合有很多优点,但实际操作中,我认为很难做到。下面是一些优点:

  • 系统可以独立地按照生命周期进行演进。

  • 系统可以使用不同的编程语言编写,并最终运行在不同的操作系统上。

  • 系统可以(并且应该)由不同的团队构建。你可以外包系统的开发。事实上,这几乎是扩展软件开发组织的唯一方法。

但也有一些缺点:

  • 在开始时需要投入更多的工作量,如果做得不好,可能永远看不到它的益处。

  • 定义API/约定相当困难,需要非常有经验的开发人员。刚开始容易做,但长期来看会很难。

  • 过度使用松耦合技术实际上会导致到处都是松散的类型。与使用清晰定义的有意义的对象相比,您可能会观察到“对象”参数或返回类型的使用增加,以及在每个类或接口上添加通用类型。这样做的坏处是平均开发人员可能会在所谓的松耦合系统的两侧使用类型间转换操作。

  • 一些松耦合技术基于接口定义的概括,旨在避免直接的依赖关系。请记住,一旦定义和发布接口,它应该被规定为不可更改的。现在,这并不是我所说的松耦合。利用JIT和方法重载等技术的.NET类可能是更好的松耦合工具。因此,这些接口和工厂的问题在于会导致类型、程序集、测试用例等的增加,而简化事情的代价是你需要构建很多系统,而不是一个系统。"N层系统是N倍的工作量" :-)

  • 松耦合机制在某种程度上绕过了编译器(C#或其他编译器)这个最强大的工具之一。这实际上是其整个目的,但它肯定存在一些缺点,因为编译器执行的所有基本工作(如类型检查等)现在需要在其他地方进行(测试),这将带来一定的成本。

  • 许多开箱即用的工具可能不再起作用。您将无法使用诸如Visual Studio“转到定义”或“查找所有引用”之类的功能。


  • +1 for "...如果你做得不好,可能永远看不到它的好处。" - 这可能是许多人不理解其好处的重要原因。 - JeroenEijkhof

    8

    松散耦合的架构有助于应用程序在需要更改或增长时进行调整。任何非平凡的应用程序都将最终需要更改或增长。

    如果您设计一个松散耦合的架构,只有应用程序的一小部分会受到要求变化的影响。而对于过于紧密耦合的架构来说,许多部分都需要更改,并且很难确定哪些部分会受到影响。

    在我看来,TDD(测试驱动开发)的主要好处之一是帮助促进松散耦合的架构。


    5
    我认为其他答案已经解释了“正确”的方法。但我将根据自己的经验写出以下内容。
    在决定架构时,有几件事情必须考虑:
    a.客户
    你有足够的时间做好一切(伟大的架构、测试等)吗?有时客户希望快速看到结果。我们可以抱怨时间短,产品不符合最高标准,但这最终是我们的问题。在这种情况下,我们向客户解释他会得到什么,并编写我们所知道的混乱代码。
    客户的要求是什么(在可靠性、可扩展性、可扩展性和速度方面)?我认为这是不言而喻的。有时客户决定什么是“正确”的。我们可以向客户提供“正确”的方式,但最终客户会决定(当然取决于时间和资金)。
    谁将在您开发完之后支持该系统?我想支持一个漂亮且松散耦合的代码。因此,当我编写代码时,我尽力使其正确。有时我可能会将视图和控制器耦合或耦合某些服务并感到满意。由于了解自己的代码,因此易于支持它。
    b.项目
    项目的规模是多少?某些项目非常小,不需要任何复杂的架构。
    软件未来是否有快速增长的机会(更多功能)?这是最大的挑战之一。但如果软件增长,这意味着它是成功的。您可能有更多资源可供使用。重构代码并使其“正确”相对容易。
    该项目是否可能存在可扩展性问题?有些项目永远不会增长,无论是用户还是数据方面。我曾见过一些项目试图通过使用Oracle RAC数据库设置来显得严肃,而简单的嵌入式数据库就足够了!
    你是开始做这个项目还是接手其他开发人员的项目?这是谁将支持软件和软件是否增长的问题的结合。您可能会从其他开发人员那里得到混乱的代码。您是否有时间和资源使其“正确”?
    c.开发团队
    团队有足够的经验使耦合正确吗?当我经验较少时,我尝试编写“正确”的代码。结果失败了。关键是真正了解您的开发团队、他们的技能和知识。不要低估这个问题。与经验较少的开发人员合作时,我通常会在架构上做出一些牺牲。将被牺牲的是我最懂得的建议。您可以牺牲架构中的某些点,但有些是不可妥协的。通常,您之前做出的一个或多个牺牲将回来咬你。

    这些开发人员是否有编写自动化测试的经验?仅拥有自动化测试是不够的。它们应该尽可能完整并且正确地完成。如果你的测试很弱,那么最好不要有任何测试。你不想依靠一个充满漏洞的墙壁。

    结论:

    我知道我们都想成为专业人士。作为专业人士,我们必须考虑所有因素。我们不能浪费时间和精力去做“正确”的事情。有时候我们必须看向其他因素(现实)并做出选择。最重要的是要接受它。


    4

    优点:

    • 可扩展性 - 允许您扩展数据库访问层
    • 可交换性 - 例如电子邮件提供程序代码
    • 易于维护 - 只需更改一个地方的代码即可
    • 易于设置单元测试 - 您可以模拟对象,如数据库

    缺点:

    • 可能需要几行额外的代码
    • 一些额外的接口类

    3

    首先,你应该编写单元测试 ;)

    假设你最终需要更改底层数据库。如果您的数据访问代码与业务逻辑紧密耦合,则可能需要付出巨大的努力。使用松散耦合的代码,您的业务逻辑将不受影响。

    如果您决定编写一些命令行实用程序来利用后端组件怎么办?使用松散耦合的代码更容易提供多个系统入口点。


    3

    这将提供可扩展性。例如,如果您有服务层,可以将其分离到几个服务器中。此外,您将拥有更少的依赖项,修改将更容易,并且代码支持也将更加容易。

    在这里,您可以看到一个有趣的小文章:SOA - Loosely Coupled...What?

    简而言之:

    松耦合系统提供了许多优点,包括支持使用后期或动态绑定到其他组件运行,并且可以调解组件结构、安全模型、协议和语义之间的差异,从而抽象出波动......


    但是如果我不需要在运行时更改组件怎么办?我无法理解您所说的可扩展性。 - Rookian
    如果您使用SOA方法,可以将服务分离到不同的服务器上工作。即使您不需要在运行时更改组件,也会从松散耦合的架构中获得优势。例如,您正在使用MS SQL Server作为数据库,并使用ADO.NET作为数据提供程序与其交互。一旦您需要迁移到Oracle甚至任何其他类型的数据存储(甚至可能是XML文件...),您将需要更新处理它的所有模块。但是,如果您将为此创建服务层,则只需修改服务层即可。 - Incognito
    通常情况下,您可以遵循一个规则 - 改变任何你想改变的东西,但不要改变合同。 - Incognito

    2

    因为即使与基于浏览器的HTTP web UI没有通信,背景中的东西可能也很有用。所以你希望能够将它与特定的UI断开连接。


    2
    主要的耦合和解耦类是为了可扩展性。其中一个类的更改不应影响另一个类。
    如果您构建一个应用程序,目前使用MYSql数据库存储数据。现在我们有新的要求将数据存储在MSSQL作为后端系统中。如果您构建的系统与MYSQL库更加集成,那么您还剩下什么解决方案呢?重新编写整个应用程序以适配MSSQL?那么这种情况下怎么办呢?我们基于MSSQL构建一个新的数据访问层(DAL),并将其插入到系统中,而不对系统(UI)进行任何更改。
    应用程序基于接口调用例程,而接口是从实现中解放出来的。
    尝试阅读Unity或MEF,这些主题将为您提供良好的见识。

    如果您不需要可扩展性,为什么要替换整个数据访问层呢? - Rookian
    如果您不需要可扩展性,那么可以按照传统方法进行工作。但在今天的范式中,人们必须始终考虑可扩展性。软件是不断发展的;需求也会发生变化。这时架构也需要相应地调整。一个很好的例子是,我们通过xxx商家处理付款,而明天我们想切换到另一个商家。处理付款的流程紧密集成到系统中,这将需要大量的工作来实现。 - Amit Bagga
    在松耦合的架构中,改动会很容易实现且不会影响主应用程序,所有变化都将在 DLL 中进行,甚至无需重新编译主 UI 代码。 - Amit Bagga
    @Rookian,可扩展性并不总是意味着要替换整个数据访问层或任何层,而是无缝地向现有应用程序添加功能,或仅更改应用程序的一部分而不影响其他已经完美工作的部分。此外,解耦使代码在项目之间具有很好的可重用性(因此类似的项目将更快地创建)。也许如果您熟悉这些架构以及为什么要这样做,您就会自己得出答案。只需花些时间阅读相关主题即可。 - Ivaylo Slavov
    感谢@lvaylo为这个话题增添更多价值。 - Amit Bagga

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接