跳转到主要内容
Chinese, Simplified

许多现代编程趋势,如微服务、容器和API,都有强烈的松耦合感。换句话说,服务现在被设计为可重复使用和可互换,而不会破坏现有的互连。几年来,软件行业一直在远离涉及紧密耦合的自定义集成。但是这些术语到底是什么意思呢?今天,我们将从一般的计算机编程角度定义什么是松散耦合和紧密耦合,并探讨这些范式对web API设计和实现意味着什么。

紧密耦合的组件是为了满足单一目的而构建的,它们相互依赖,并且不容易重复使用。

什么是耦合?

从广义上讲,连接的软件服务要么更紧密地耦合,要么更松散地耦合。紧密耦合是将资源绑定到特定目的和功能的概念。紧密耦合的组件可以将每个资源绑定到特定用例、特定接口或特定前端。

本质上,一个紧密耦合的系统是专门构建的,每一个偏离标准的定制都有其自身的资源和集成。这是建立数据关系的一种简单方法,在理解、依赖和交互所述信息方面是有益的。然而,紧密耦合给软件的可扩展性和可扩展性带来了明显的损失。

然而,松散耦合是相反的范式。在一个松散耦合的系统中,组件是相互分离的。每个资源都可以有多个前端或应用程序。这些元素中的每一个都是相反的。所有系统都可以独立工作,作为较大系统组的一部分,也可以与多个分段系统组紧密配合。最终,任何东西都不会被迫与其他任何东西建立关系,这在可扩展性和可扩展性方面带来了明显的好处。需要注意的是,总体复杂性往往会增加。

以无头CMS为例说明松耦合。无头CMS将后端与前端分离,这意味着开发人员可以从任何客户端浏览器、平台或设备重复使用并与相同的启用API的后端交互。

松耦合作为最佳实践

loose coupling in computer programming

解耦或松耦合的组件更加独立和可重用,从而提高了整体可扩展性。

松耦合和紧耦合是相当普遍的概念。那么,在实践中,哪些用例可以从每种方法中受益呢?

在我们讨论紧密耦合之前,应该注意的是,松散耦合是迄今为止RESTful API开发最理想的范式。RESTful API应该能够在多个资源中从一个用例转换、重新混合、扩展和变形到另一个用例。从本质上讲,一个合适的REST实现应该是分层的和无状态的——它应该在很大程度上独立于单个用例存在,并且应该用接口处理所有请求所需的所有信息来响应。

即使在真实RESTful设计的需求之外,松散耦合也是大多数现代用例的优秀实现。任何API类型都应该需要最少的新工作来利用,允许开发人员在多个实现和接口中使用他们的知识。更少的自定义实现意味着在构建核心功能上花费更多的精力,而不是为每个新资源构建单独的应用程序和接口。

此外,松耦合带来了一些显著的安全性提升。由于每个资源不存在一百万个不同接口的一百万个不同版本,因此需要保护和更新的数据量可能会大大减少。实际上,松散耦合导致了一个更加安全的生态系统。

最后,智能组件和哑组件在很大程度上相互隔离,减少了过度获取。换句话说,需要更多知识或数据才能运行的智能组件可以通过与集中式API对话来实现。不需要知道太多的愚蠢组件可以独立运行,而不会与耦合的、独特的资源或需求发生冲突。

最终,松耦合会降低组件之间的相互依赖性,并降低破坏更改的风险。

紧耦合作为一种替代方案

如果松耦合有这么多好处,为什么还要考虑紧耦合?虽然紧耦合确实不是大多数微服务和RESTful设计的最佳选择,但它在特定的应用程序中确实有一席之地。

松耦合的主要好处是资源与接口解耦,以允许更多可互操作、可扩展的API和资源模式。然而,并不是所有的服务都需要这样。在某些情况下,一个人有一个单一的资源和一个单一类型的单一接口。在这种情况下,通用API通常与单个用例和单个工作流绑定。

松耦合还会给系统带来更大的复杂性。某些工作流仅使用一个设备、一个接口和一个API。如果只计划一个实现,那么紧密耦合可以使开发工作简单得多。除非应用程序很有可能会转向更通用的东西,否则简单性是首选。

非永久性耦合

虽然紧耦合和松耦合无疑是最明显的类型,但还有另一种类型的耦合。从技术上讲,非永久耦合是一种混合型耦合,不属于这两类。非永久耦合可以使用基于时间或位置的因素来耦合或解耦组件。在实践中,它仍然经常属于松散或紧密耦合的保护伞下,但这种行为足够独特,可以在这里单独定义。

时间耦合是指当另一个资源已经响应了初始请求时,资源只能由一个资源使用。资源和接口是完全解耦的,直到创建了一个有时间限制的耦合。虽然大多数情况下都应该避免这种情况,但在一些例子中这可能是有效的。例如,管理对工作空间的远程访问的安全应用程序可以使用时间耦合来限制对电梯连接API的访问,直到凭证服务授权请求用户为止。

与此相关的是位置耦合,这是一种将资源耦合到依赖于两者接近程度的接口的范例。例如,本地服务API可能与资源紧密耦合,例如要求某人在办公室,并通过已知和可信的机器访问以访问资源。在这种情况下,只有当用户接近地理上有边界的区域中的资源时,才会创建耦合。

松耦合和紧耦合实施

关于这两种范式在实践中如何工作的示例,可以在C#Corner中找到一个优秀的代码示例。我们在下面复制它以供评论。

让我们从一个简单的紧耦合情况开始。想象一下,我们正在用C#编写一个远程控制应用程序。以下代码表示这种情况:

namespace
TightCoupling
{
    public class Remote
    {
       private  Television Tv  { get; set;}
       protected Remote()
       {
           Tv = new Television();
       }
       static Remote()
       {
           _remoteController = new Remote();
       }
       static Remote _remoteController;
       public static Remote Control
       {
           get
           {
               return _remoteController;
           }
       }
         public void RunTv()
         {
             Tv.Start();
         }
    }
}

这种方法的明显好处是简单——在少数代码行中,我们就有了一个非常简单的远程控制器框架。也就是说,这种方法存在重大问题。通过将接口(遥控器)和资源(电视)紧密耦合,可以创建一种关系,在这种关系中,没有另一个,这两个元素中的任何一个都无法发挥作用。没有遥控器就无法更换电视,遥控器除了控制电视之外无法控制任何东西,任何一种更改都会直接影响另一种。如果电视和遥控器是这个生态系统中唯一的设备,这可能是可以接受的。然而,如果制造商想要更大的可扩展性来控制其他设备,那么在当前的方法中这是不可能的。

为了解决这个问题,让我们考虑一种更松散耦合的方法。以下代码是远程实例的远程类和管理类:

public interface IRemote
    {
        void Run();
    }
public class Television : IRemote
    {
        protected Television()
        {
        }
        static Television()
        {
            _television = new Television();
        }
        private static Television _television;
        public static Television Instance
        {
            get
            {
                return _television;
            }
        }
        public void Run()
        {
            Console.WriteLine("Television is started!");
        }
    }
public class Remote
    {
         IRemote _remote;
        public Remote(IRemote remote)
        {
            _remote = remote;
        }

        public void Run()
        {
            _remote.Run();
        }
    }

这种方法需要一块代码用于实际使用,如下所示:

class Program
{
        static void Main(string[] args)
        {
            Remote remote = new Remote(Television.Instance);
            remote.Run();
            Console.Read();
        }
    }

这里的主要缺点从一开始就很明显——代码比更直接的紧耦合方法要复杂得多。也就是说,它获得了一些显著的好处。

首先,它将可用性和可扩展性扩展到了一个更高的级别。通过将遥控器和电视相互抽象,可以添加、扩展和开发新的功能,而基本上不会对资源和接口产生影响。这本质上是将整个接口和数据流模块化,允许在许多不同的级别进行开发。

另一个主要的好处是,每个组件都可以单独进行测试和操作。在我们最初的紧密耦合范式中,我们的测试方法受到限制,因为我们只能检查整个数据流。随着一切模块化,我们可以测试每个单独的组件。

也许这里最大的好处是我们可以合并和扩展每个类。例如,让我们假设我们的遥控器不仅仅用于电视——我们可能想控制空调、智能扬声器或冰箱。如果我们使用IR或蓝牙模块,我们可能希望将所有命令输入到单个发射器函数中。虽然具体的命令显然会有所不同,但基本功能仍然相同。对于松散耦合的系统,这些功能可以组合成一个单一的接口,但它们本身可以是具有不同可扩展功能的不同代码集的一部分。

在紧密耦合的范式中,这些单独的功能中的每一个都需要针对特定的用例进行明确的开发,这几乎比仅仅在松散的范式中工作更复杂。

实践中的耦合示例

让我们考虑一些真实世界的例子,更好地了解紧耦合和松耦合之间的区别。紧密耦合的一个例子是经典的单片软件体系结构设计模式。尽管单片设计通常被认为是过时的,但它们确实有一些实际用途。例如,亚马逊Prime Video的开发人员最近分享了将他们的设计重构为一个更为单一的模式以降低成本和提高可扩展性的意义。

单片设计与微服务架构相反,在微服务架构中,组件更加独立和分布式。例如,Soundcloud花了数年时间打破其单一的基础设施,采用更解耦的、基于API的服务。微服务架构通常使用Kubernetes等云原生平台来管理分布式、基于容器的服务的部署和协调。

松散耦合还实现了现代网络安全不可或缺的关注点分离哲学。例如,实现API安全性是将尽可能多的安全功能外部化的最佳实践。现代OAuth流通常包含一个外部授权服务器,该服务器可以验证和发布JSON web令牌。以这种方式分离安全问题有助于支持最小权限访问模型。

最佳选择

通常,我们的建议是“这取决于……查看您的用例,然后决定每个选项是否合适。”然而,这是我们更直接的少数情况之一:紧密耦合几乎总是不适合RESTful API设计。解耦组件通常会带来更好的可扩展性,并有助于系统经得起未来考验。仅在极少数情况下(通常为非REST)才建议使用紧密耦合。在这种情况下,不要害怕使用紧耦合,而是要考虑你最初的假设是否正确。

你觉得耦合怎么样?这是对每种范式的优点和缺点的公平评估吗?请在下面的评论中告诉我们。

原文地址
https://nordicapis.com/the-difference-between-tight-coupling-and-loose-coupling/
本文地址
Article

微信

知识星球

微信公众号

视频号