面向对象范式中的松散耦合和紧密耦合之间有什么区别?


Answers:


338

紧密耦合是一组类彼此高度依赖时。

当班级承担太多职责时,或者当一个关注点分散到多个班级而不是拥有自己的班级时,就会出现这种情况。

松耦合是通过促进单一责任和关注点分离的设计实现的。

松散耦合的类可以独立于其他(具体)类使用和测试。

接口是用于解耦的强大工具。类可以通过接口而不是其他具体类进行通信,并且任何类都可以通过实现接口而位于通信的另一端。

紧密耦合的示例:

class CustomerRepository
{
    private readonly Database database;

    public CustomerRepository(Database database)
    {
        this.database = database;
    }

    public void Add(string CustomerName)
    {
        database.AddRow("Customer", CustomerName);
    }
}

class Database
{
    public void AddRow(string Table, string Value)
    {
    }
}

松耦合的示例:

class CustomerRepository
{
    private readonly IDatabase database;

    public CustomerRepository(IDatabase database)
    {
        this.database = database;
    }

    public void Add(string CustomerName)
    {
        database.AddRow("Customer", CustomerName);
    }
}

interface IDatabase
{
    void AddRow(string Table, string Value);
}

class Database implements IDatabase
{
    public void AddRow(string Table, string Value)
    {
    }
}

这里的另一个例子。


到目前为止,您所说的都是很合理的。您能否解释一下耦合机制如何与观察者模式相关?
吉姆(Jim)2010年

1
此处描述了观察者模式:en.wikipedia.org/wiki/Observer_pattern。由于Subject类可以维护从“ Observer”继承的类的列表,而无需实际知道这些类的具体类型,因此这是松耦合的一个实例。主题不依赖于其任何观察者或其内部关注点。观察者不依赖于主题或其任何关注点。
乔纳森

4
Java接口是可以帮助实现这一目标的工具。但是,它们不是必需的。接口的程序而不是实现的概念意味着对公共方法/属性(或当语言不支持访问修饰符,如C时,供外部调用者使用的方法)的程序。该接口应该通过不更改来履行此隐含合同。在没有访问修饰符的语言(如C)中,这意味着仅使用已发布的接口/函数,而不使用将在内部使用的接口/函数,因为那些接口/函数可能会根据需要更改以支持已发布的函数。
Bill Rosmus '16

3
@jonathanconway谢谢您,先生,但是两段代码都做同样的事情:它们之间有什么区别?即,松散耦合有什么优势?
BKSpurgeon '16

1
我以前已经(基本上)看过这个例子。我认为,仅当要使用多种类型的数据库时,这并不重要。如果您计划只有一个,那么直接使用实现会不好吗,然后如果您需要注入不同的类型,然后重构使用数据库的单一用法来使用接口,那会不好吗?而不是增加可能不需要的样板代码负载?
卡洛斯·布里比斯卡斯

177

没有任何代码的说明

摘要示例:

帽子与身体“松散地耦合”。这意味着您可以轻松脱下帽子,而无需对人/身体进行任何更改。如果可以做到这一点,那么您将拥有“松散耦合”。详见下文。

帽子与身体“松散地耦合”。 这意味着您可以轻松脱下帽子,而无需对人/身体进行任何更改。 图片归因:https://pixabay.com/cn/greeting-cylinder-chapeau-dignity-317250/

紧耦合(详细示例)

想想你的皮肤。它粘在你的身上。它像手套一样合身。但是,如果您想将肤色从白色变为绿色怎么办?您能想象脱皮,染色,再粘贴回去会多么痛苦吗?更换皮肤很困难,因为它与您的身体紧密相连。您只是无法轻松进行更改。您必须从根本上重新设计人才能使之成为可能。

  • 关键点1:换句话说,如果您要换皮肤,也必须同时改变您的身体设计,因为两者紧密结合在一起。

上帝不是一个好的面向对象的程序员。

松耦合(详细示例)

现在想想早上穿衣服。你不喜欢蓝色吗?没问题:您可以穿一件红色的衬衫代替。您可以轻松而轻松地完成此操作,因为衬衫与皮肤的连接方式实际上与您的身体没有真正的连接。这件衬衫不知道也不在乎它在干什么。换句话说,您可以在不真正改变身体的情况下更换衣服。

  • 这是关键点2。如果您换了衬衫,那么就不必强迫自己换衣服了 -当您可以这样做时,便会出现松散的耦合。如果您无法做到这一点,那么您将拥有紧密的联系。

简而言之,这就是基本概念。

为什么所有这些都很重要?

这很重要,因为软件一直在变化。一般来说,您希望能够轻松修改代码而不更改代码。我知道这听起来很矛盾,但请多多包涵。

编码时的耦合实例

  • CSV / JSON / DB示例:如果有人希望将其输出以CSV文件而不是JSON等格式存储,或者如果您想从MySQL切换到PostGreSQL,则应该能够非常轻松地在代码中进行这些更改,而无需重写换句话说,您不想将应用程序与特定的数据库实现(例如Mysql)或特定的输出(例如CSV文件)紧密耦合。因为,正如软件中不可避免的那样,更改将到来。当它们出现时,如果您的代码部分松散耦合,则容易得多。

  • 汽车零件示例:如果有人想要用黑色驾驶汽车,那么您不必为此重新设计整个汽车。汽车及其零配件将是松散耦合架构的完美示例。如果您想用更好的引擎更换引擎,则应该能够轻松卸下引擎,而将其更换为更好的引擎。如果您的汽车仅适用于劳斯莱斯1234发动机,而不适用于其他发动机,那么您的汽车将与该发动机紧密相连(劳斯莱斯1234)。如果您更改汽车的设计,使其与任何汽车兼容,那会更好。引擎,使其与组件之间的连接更加松散。如果您的汽车完全不需要引擎就可以工作,那就更好了!某种程度的耦合将要发生,但您应努力将其最小化。为什么?因为当需求发生变化时,我们仍然应该能够非常快速地交付高质量的软件,并且通过松耦合可以帮助我们实现该目标。

摘要

简而言之,松散的耦合使代码更易于更改。上面的答案提供了一些此时值得阅读的代码。

多态性和SOLID原理

回复:@TimoHuovinen评论-松耦合的概念与多态性紧密相关。如果您掌握了衬衫/汽车部件的基本类比,那么您将可以理解多态性。最好的方法是,在此线程的其他答案中,阅读我可估计的同事提供的代码示例。如果我再说一遍,您可能会得到太多信息。

图片归因


8
对于新程序员来说,这是一个被低估的解释。艰难地经历一些其他人提出的大话,一旦您理解了基础知识,那么更容易理解大话大声笑
user2763557

6
有些事物必须与环境紧密耦合,而有些则必须与其环境松散耦合。使用皮肤不是紧密耦合的适当类比。如果认为皮肤与身体紧密相连,那么其他部分也是如此。身体(作为一个整体)必须具有零件(紧密集成)才能正常运行(可能是自然界的想法-一个出色的建筑师)。如果将这些零件设计成可更换的(就像更换帽子一样简单),那么“人体”的含义就失去了其定义。评论1/2。
lupchiazoem '18

6
举个例子,如果皮肤是可更换的,那么头部也必须重新更换。如果发生这种情况,那么从一次见面到另一次见面可能就无法认清人们。一个很好的紧密/松散耦合比喻是-汽车及其零件,计算机及其零件等...如果计算机的鼠标/键盘有问题,可以将其替换为另一个零件,而不是使整个计算机变得无用扔掉 评论2/2。
lupchiazoem '18

1
@BKSpurgeon很棒的解释!!而且您以示例方式进行解释也非常好。
基兰·乔希

5
非常有创意地解释。我与这个答案紧密相关。
AliN11

72

在面向对象的设计中,耦合的数量是指一个类的设计在多大程度上取决于另一类的设计。换句话说,A级的变化多久与B级的力变化有关?紧耦合意味着这两个类别经常一起改变,松耦合意味着它们大多是独立的。通常,建议使用松散耦合,因为它更易于测试和维护。

您可能会发现Martin Fowler(PDF)的这篇论文很有帮助。


“ A级变更多久与B级变更相关?” 我需要上述句子的简短示例吗?
Kumaresan Perumal

15

通常,紧密耦合在很多时候是不好的,但是在大多数情况下,因为它降低了代码的灵活性和可重用性,使更改变得更加困难,阻碍了可测试性等。

紧密耦合对象是一个需要彼此了解很多的对象,并且通常高度依赖于彼此的接口。在紧密耦合的应用程序中更改一个对象通常需要对许多其他对象进行更改。在小型应用程序中,我们可以轻松地识别出更改,而错过任何事物的机会则更少。但是在大型应用程序中,并不是每个程序员都知道这些相互依存关系,否则就有机会错过更改。但是,每组松散耦合的对象都不依赖于其他对象。

简而言之,松耦合是一个设计目标,旨在减少系统组件之间的相互依赖性,以降低一个组件中的更改将需要更改任何其他组件的风险。松散耦合是一个更为通用的概念,旨在提高系统的灵活性,使其更易于维护,并使整个框架更“稳定”。

耦合是指一个元素具有另一元素的直接知识程度。我们可以说一个例子:A和B,只有当A改变其行为时,只有B改变其行为。松散耦合的系统很容易分解为可定义的元素。


11

当两个对象松散耦合时,它们可以交互,但彼此之间的知识很少。

松散耦合的设计使我们能够构建可以应对变化的灵活的OO系统。

观察者设计模式是使类松散耦合的一个很好的例子,您可以在Wikipedia上进行查看。


6

我关于耦合的博客文章的摘录:

什么是紧耦合

如上定义,紧密耦合对象是需要了解其他对象的对象,通常高度依赖于彼此的接口。

当我们在紧密耦合的应用程序中更改一个对象时,通常需要更改许多其他对象。在小型应用程序中没有问题,我们可以轻松识别更改。但是,在大型应用程序中,并非每个消费者或其他开发人员都知道这些相互依存关系,否则将来会有很多改变的机会。

让我们以购物车演示代码来了解紧密耦合:

namespace DNSLooseCoupling
{
    public class ShoppingCart
    {
        public float Price;
        public int Quantity;

        public float GetRowItemTotal()
        {
            return Price * Quantity;
        }
    }

    public class ShoppingCartContents
    {
        public ShoppingCart[] items;

        public float GetCartItemsTotal()
        {
            float cartTotal = 0;
            foreach (ShoppingCart item in items)
            {
                cartTotal += item.GetRowItemTotal();
            }
            return cartTotal;
        }
    }

    public class Order
    {
        private ShoppingCartContents cart;
        private float salesTax;

        public Order(ShoppingCartContents cart, float salesTax)
        {
            this.cart = cart;
            this.salesTax = salesTax;
        }

        public float OrderTotal()
        {
            return cart.GetCartItemsTotal() * (2.0f + salesTax);
        }
    }
}

上面的例子有问题

紧密耦合会产生一些困难。

在这里,OrderTotal()方法为我们提供了购物车中当前物品的完整金额。如果我们要在此购物车系统中添加折扣功能。在上面的代码中很难做到这一点,因为我们必须在每个类上进行更改,因为它们之间的耦合非常紧密。


6

松耦合意味着两个组件之间的依赖程度非常低。
示例:GSM SIM

紧密耦合意味着两个组件之间的依赖程度非常高。
示例:CDMA Mobile


5

据我了解,与松散耦合的体系结构相比,紧密耦合的体系结构不能为更改提供很大的灵活性。

但是在松散耦合的体系结构,消息格式或操作平台或修改业务逻辑的情况下,不会影响另一端。如果关闭系统进行改造,那么另一端当然将不能再访问该服务一段时间,但除此之外,未更改的另一端可以像进行改造之前那样恢复消息交换。


5

紧密耦合意味着一个类别依赖于另一个类别。
松耦合意味着一类依赖于接口而不是类。

紧密耦合中,方法中声明了硬编码的依赖项。
松耦合中,我们必须在运行时从外部传递依赖关系,而不是硬编码。(松耦合系统使用接口来减少对类的依赖。)

例如,我们有一个系统可以通过两种或多种方式发送输出,例如JSON输出,CSV输出等。

紧耦合

public interface OutputGenerator {
    public void generateOutput();
}

public class CSVOutputGenerator implements OutputGenerator {
    public void generateOutput() {
        System.out.println("CSV Output Generator");
    }
}

public class JSONOutputGenerator implements OutputGenerator {
    public void generateOutput() {
        System.out.println("JSON Output Generator");
    }
}

// In Other Code, we write Output Generator like...
public class Class1 {
    public void generateOutput() {
        // Here Output will be in CSV-Format, because of hard-coded code.
        // This method tightly coupled with CSVOutputGenerator class, if we want another Output, we must change this method.
        // Any method, that calls Class1's generateOutput will return CSVOutput, because Class1 is tight couple with CSVOutputGenerator.
        OutputGenerator outputGenerator = new CSVOutputGenerator();
        output.generateOutput();
    }
}

在上面的示例中,如果要更改JSON中的输出,则需要查找并更改整个代码,因为Class1与CSVOutputGenerator类紧密耦合。

松耦合

public interface OutputGenerator {
    public void generateOutput();
}

public class CSVOutputGenerator implements OutputGenerator {
    public void generateOutput() {
        System.out.println("CSV Output Generator");
    }
}

public class JSONOutputGenerator implements OutputGenerator {
    public void generateOutput() {
        System.out.println("JSON Output Generator");
    }
}

// In Other Code, we write Output Generator like...
public class Class1 {
    public void generateOutput(OutputGenerator outputGenerator) {
        // if you want to write JSON, pass object of JSONOutputGenerator (Dependency will be passed externally to this method)
        // if you want to write CSV, pass object of CSVOutputGenerator (Dependency will be passed externally to this method)

        // Due to loose couple with class, we don't need to change code of Class1, because Class1 is loose coupled with CSVOutputGenerator or JSONOutputGenerator class
        // Any method, that calls Class1's generateOutput will desired output, because Class1 does not tight couple with CSVOutputGenerator or JSONOutputGenerator class
        OutputGenerator outputGenerator = outputGenerator;
        output.generateOutput();
    }
}

3

有某些工具可以通过其库提供依赖项注入,例如,在.net中,我们有ninject Library

如果要进一步使用Java,则spring提供了此功能。

松耦合的对象可以通过在代码中引入接口来实现,这就是这些源代码所做的。

在您的代码中说您正在编写

Myclass m = new Myclass();

现在,您的方法中的此语句说您依赖myclass于此,称为紧密耦合。现在,您提供了一些构造函数注入,或属性注入和实例化对象,那么它将变得松散耦合。


3

这里使用类比有很多不错的答案,但是工作中的一位朋友给了我一个例子,我比这里提到的所有例子都更喜欢……眼睛和眼镜!

紧耦合

紧密的耦合将是眼睛。如果我想固定视力,则进行眼科手术的费用并不昂贵,而且存在很大的风险。但是,如果设计师(人类)找到了更好的方法,该怎么办。添加与身体松散耦合的功能,以便可以轻松更改!(是..眼镜)

松耦合

我可以轻松更换眼镜,而又不会破坏我的基本视野。我可以摘下眼镜,而我的目光将是以前的样子(不坏不坏)。使用不同的眼镜会改变我们通过眼睛观察世界的方式,风险很小且易于维护。

摘要

因此,下次有人问您“谁在乎我的代码是否紧密耦合?” 答案全在于变革的努力,维护的努力和变革的风险。

那么如何在C#中完成呢?接口和依赖注入!

编辑

这也是装饰器模式的一个很好的例子,通过满足界面要求,眼睛却是我们正在装饰的类,但是具有不同的功能(例如太阳镜,老花镜,珠宝商的放大镜等)


2

松散耦合是对旧样式的硬编码依赖项和相关问题(例如,当任何更改和代码重用时频繁重新编译)的解决方案,并且可以解决这些问题。它强调在组件中实现工作程序逻辑,并避免在其中使用解决方案特定的接线代码。

松散耦合= IoC 参见以获得更简单的说明。


3
我不认为松散耦合与控制反转相同。控制反转是减少设计耦合的一种非常有用的技术,但是还有许多其他技术。
唐·柯比

2

松耦合是在不直接提供依赖的紧密耦合的情况下,不直接提供依赖类的所有信息(即在from的from界面中)而不提供依赖的所有信息的过程,这种紧密耦合不是很好的编码方式。


2

它是关于另一个依赖的类的依赖率,它的松散耦合性很低,而紧密耦合性很高。要在明确的面向服务架构,服务是松散耦合的,彼此对单片哪些类依赖于对方是故意的


1

如果对象的创建/存在依赖于无法定制的另一个对象,则其紧密耦合。并且,如果可以定制依赖项,则其松散耦合。考虑一下Java中的示例:

class Car {

    private Engine engine = new Engine( "X_COMPANY" ); // this car is being created with "X_COMPANY" engine
    // Other parts

    public Car() { 
        // implemenation 
    }

}

Car类的客户端只能使用“ X_COMPANY”引擎创建一个。

考虑打破这种耦合并具有以下能力:

class Car {

    private Engine engine;
    // Other members

    public Car( Engine engine ) { // this car can be created with any Engine type
        this.engine = engine;
    }

}

现在,a Car不再依赖于“ X_COMPANY”引擎,因为它可以使用类型创建。

特定于Java的注释:仅将Java接口用于去耦是不合适的设计方法。在Java中,接口有一个目的-充当契约,本质上提供去耦行为/优势。

比尔·罗斯莫斯(Bill Rosmus)在接受的答案中的评论有很好的解释。


0

紧密耦合意味着类和对象相互依赖。通常,紧密耦合通常不好,因为它会降低代码的灵活性和可重用性,而松散耦合意味着减少直接使用不同类的类的依赖关系。

紧密耦合紧密耦合的对象是需要了解其他对象的对象,通常高度依赖于彼此的接口。在紧密耦合的应用程序中更改一个对象通常需要更改许多其他对象。在小型应用程序中,我们可以轻松识别更改,并且错过任何内容的机会都更少。但是在大型应用程序中,并不是每个程序员都知道这些相互依存关系,因此有可能忽略更改。例:

    class A {
       public int a = 0;
       public int getA() {
          System.out.println("getA() method");
          return a;
       }
       public void setA(int aa) {
          if(!(aa > 10))
             a = aa;
       }
    }
    public class B {
       public static void main(String[] args) {
          A aObject = new A();
          aObject.a = 100; // Not suppose to happen as defined by class A, this causes tight coupling.
          System.out.println("aObject.a value is: " + aObject.a);
       }
    }

In the above example, the code that is defined by this kind of implementation uses tight coupling and is very bad since class B knows about the detail of class A, if class A changes the variable 'a' to private then class B breaks, also class A's implementation states that variable 'a' should not be more than 10 but as we can see there is no way to enforce such a rule as we can go directly to the variable and change its state to whatever value we decide.

    Output
    aObject.a value is: 100

Loose Coupling
Loose coupling is a design goal to reduce the inter-dependencies between components of a system with the goal of reducing the risk that changes in one component will require changes in any other component.
Loose coupling is a much more generic concept intended to increase the flexibility of the system, make it more maintainable and makes the entire framework more stable.
Example:

class A {
   private int a = 0;
   public int getA() {
      System.out.println("getA() method");
      return a;
   }
   public void setA(int aa) {
      if(!(aa > 10))
         a = aa;
   }
}
public class B {
   public static void main(String[] args) {
      A aObject = new A();
      aObject.setA(100); // No way to set 'a' to such value as this method call will
                         // fail due to its enforced rule.
      System.out.println("aObject value is: " + aObject.getA());
   }
}

在上面的示例中,由这种实现定义的代码使用松散耦合,因此建议使用该代码,因为类B必须经过类A才能获得其强制执行规则的状态。如果内部更改了A类,则B类不会中断,因为它仅使用A类作为通信方式。

Output
getA() method
aObject value is: 0
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.