在面向对象范式中,松耦合和紧耦合的区别是什么?

在面向对象范式中,有人能准确地描述松耦合和紧耦合之间的区别吗?

386661 次浏览

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

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

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

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

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

紧密耦合的例子:

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)
{
}
}

另一个例子在这里

在面向对象设计中,耦合量指的是一个类的设计依赖于另一个类的设计的程度。换句话说,A类力的变化与B类力的变化相关的频率是多少?紧耦合意味着两个类经常一起更改,松耦合意味着它们大部分是独立的。一般来说,推荐使用松耦合,因为它更容易测试和维护。

你可能会发现马丁·福勒的论文(PDF)很有用。

松耦合是老式硬编码依赖关系和相关问题的答案,比如当任何事情发生变化时频繁重新编译和代码重用。它强调在组件中实现工作者逻辑,避免解决方案特定的连接代码。

松耦合= IoC

.

.

.
当两个对象松散耦合时,它们可以相互作用,但对它们知之甚少 对方。< / p >

松散耦合设计允许我们构建能够处理变更的灵活的OO系统。

观察者设计模式是一个让类松散耦合的好例子,你可以在维基百科中看到它。

有一些工具通过它们的库提供依赖注入,例如在。net中我们有ninject图书馆

如果你在java中更进一步,那么春天提供了这个功能。

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

在你正在编写的代码中

Myclass m = new Myclass();

现在你的方法中的这个语句说你依赖于myclass,这被称为紧耦合。现在你提供了一些构造函数注入,或者属性注入实例化对象,然后它就会变得松散耦合。

一般来说,紧密耦合是不好的,但大多数时候,因为它降低了代码的灵活性和可重用性,它使更改更加困难,它阻碍了可测试性等。

紧耦合对象是一种需要相互了解相当多的对象,通常高度依赖于彼此的接口。在紧密耦合的应用程序中更改一个对象通常需要更改许多其他对象,在小型应用程序中,我们可以很容易地识别更改,并且很少有机会错过任何东西。但在大型应用程序中,并非每个程序员都知道这些相互依赖关系,或者有机会错过更改。但是每组松散耦合的对象都不依赖于其他对象。

简而言之,我们可以说,松耦合是一种设计目标,它寻求减少系统组件之间的相互依赖关系,目的是降低一个组件的更改将要求任何其他组件进行更改的风险。松散耦合是一个更通用的概念,旨在增加系统的灵活性,使其更可维护,并使整个框架更“稳定”。

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

我的理解是,与松散耦合的体系结构相比,紧密耦合的体系结构并没有为更改提供很多灵活性。

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

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

紧密耦合:-是什么

正如上面定义的那样,紧密耦合对象是一个需要了解其他对象的对象,并且通常高度依赖于彼此的接口。

当我们更改紧耦合应用程序中的一个对象时,通常需要更改许多其他对象。在一个小的应用程序中没有问题,我们可以很容易地识别变化。但在大型应用程序中,这些相互依赖关系并不总是为每个消费者或其他开发人员所了解,或者将来可能会有很多变化。

让我们用购物车演示代码来理解紧耦合:

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()方法为我们提供购物车中当前项目的完整金额。如果我们想在这个购物车系统中添加折扣功能。在上面的代码中很难做到这一点,因为我们必须在每个类中进行更改,因为它是非常紧密耦合的。

无代码说明

用简单的类比来解释概念。代码可以稍后再写。

松耦合的例子:

The Hat is .

在上图中,这顶帽子是“松耦合”的;对身体。这意味着你可以很容易地摘下帽子,而不需要对人/身体做任何改变。当你能做到这一点时,你就有了“松耦合”。详情见下文。

详细的例子

想想你的皮肤。它粘在你身上了。它非常合适。但是如果你想把你的肤色从白色变成黑色呢?你能想象剥掉你的皮肤,染色,然后再贴回去有多痛苦吗?改变你的皮肤是困难的,因为它与你的身体紧密相连。你只是不能轻易做出改变。为了使这成为可能,你必须从根本上重新设计一个人。

  • 关键点1:换句话说,也是如果你想要改变皮肤,你也必须改变你身体的设计,因为这两个是连接在一起的——它们是紧密耦合的。

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

松耦合(详细示例)

现在想想早上穿衣服。你不喜欢蓝色?没问题:你可以换一件红衬衫。你可以轻松轻松地做到这一点,因为衬衫并不像皮肤那样真正地连接在你的身体上。衬衫不知道也不关心它穿在什么身体上。换句话说,你可以改变你的衣服,而不需要真正改变你的身体。

  • 这是第二点。如果你换了衬衫,那么你就不会被迫改变你的身材 -当你可以这样做,那么你有松耦合。当你不能这样做时,你就有了紧密耦合。

这是一个简单的基本概念。

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

在编写软件时,更改是不可避免的。如果我们提前知道变更将发生在某个特定的地方,那么我们应该确保我们的软件在那个特定的点上是松散耦合的,因为这将使我们能够轻松快速地进行这些更改,而没有错误.....这意味着什么呢?看一些例子:

软件中的松耦合:

  • 在我职业生涯的早期,我的经理说:“给我一个CSV文件的输出”。太好了。我开始努力,创造了一个像魔法一样有效的日常工作。然后一两个星期后,他说:“实际上,我想用json为另一个客户输出”。

真痛苦。我不得不重写整个剧本。我有点知道会发生这种情况,所以我用接口重写了整个程序——一种松散耦合的设计模式,现在,添加了新的输出格式,进行更改就容易多了。我可以编辑JSON部分,而不用担心我会破坏我的CSV输出。

另一个重要的点:软件变更是正常的。在变化点上松散耦合。

  • DB例子:如果你想轻松地从sqlLite切换到PostGreSQL -松散耦合代码使它很简单切换(即穿上一件红色衬衫而不是一件蓝色衬衫)。Rails ActiveRecord库在其数据库实现上是松散耦合的。这使得使用相同的代码库时使用自己的数据库实现非常容易!

  • 或者如果你正在使用AWS,他们因为市场主导地位而开始收取过高的费用,你应该能够很容易地切换到谷歌或Azure等。这正是像Active Storage这样的库存在的原因——它们让用户对正在使用的特定云提供商(Azure、AWS S3、GCS等)有一种健康的漠不关心。只需更改一行代码,就可以轻松更改云提供程序。云存储提供商的实现细节是松散耦合的。

  • 测试:如果你想测试你的软件,用预定的输出和输入-你要怎么做?使用松耦合软件——这是轻而易举的事情:您可以运行您的测试,还可以部署您的生产代码,并在相同的代码库中完成所有这些工作。使用紧密耦合的代码,测试您的产品代码几乎是不可能的。

我们需要让一切都“松散耦合”吗?可能不会。我们必须运用我们的判断力。一定程度的耦合是不可避免的。但是考虑最小化它如果你事先知道,它会改变。我也会建议反对猜测,在那里事情会发生变化,并且松散耦合一切。松散的情侣,只在你需要的时候。

总结

简而言之,松耦合使代码更容易更改。

上面的答案提供了一些值得一读的代码。

高级的主题

松耦合与多态性和接口密切相关。如果你喜欢漫画和类比,看看我写过的其他文章:

图片归因

松耦合是在不提供依赖关系的所有信息的情况下,间接地给出类所需要的依赖关系的过程。E在from的接口中),以防紧耦合,你直接给出依赖关系,这不是编码的好方法。

松散耦合表示两个组件之间的依赖程度很低 例如:GSM SIM

紧密耦合是指两个组件之间的依赖程度非常高 例如:CDMA Mobile

它是关于类依赖率到另一个类,这个类在松散耦合中非常低,在紧密耦合中非常高。在面向服务的架构中要明确的是,服务之间是松散耦合的对单片< em > < / em >,这些类相互依赖是有意的

紧密耦合表示一个类依赖于另一个类。
松耦合表示一个类依赖于接口而不是类。

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

例如,我们有一个系统可以以两种或多种方式发送输出,如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();
}
}

如果一个对象的创建/存在依赖于另一个不能被剪裁的对象,它的紧密耦合。如果依赖关系可以被裁剪,那么它的松散耦合。考虑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;
}


}

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

Java特有的注意事项:使用Java接口只是为了解耦合不是一个合适的设计方法。在Java中,接口有一个目的——充当契约,从本质上提供解耦合行为/优势。

Bill Rosmus在公认答案中的评论有一个很好的解释。

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

紧密耦合

紧密耦合是眼睛。如果我想修复我的视力,做眼部移植手术非常昂贵,而且有相当大的风险。但如果设计师(作为人类)找到了更好的方法呢?添加一个与主体松散耦合的特性,这样就可以轻松地更改它!(是的. .眼镜)

松散耦合

我可以很容易地更换我的眼镜,而不会破坏我的潜在视力。我可以摘下眼镜,我的视力就会恢复到以前的水平(不是更好或更差)。使用不同的眼镜改变了我们通过眼睛看世界的方式,而且风险很小,易于维护。

总结

所以下次有人问你“谁在乎我的代码是否紧密耦合?”答案都是关于努力改变,努力维持和改变的风险。

那么这在c#中是如何实现的呢?接口和依赖注入!

编辑

这也是Decorator模式的一个很好的例子,其中眼睛是我们通过满足界面需求来装饰的类,但提供了不同的功能(例如太阳镜、老花镜、珠宝商的放大镜等)。

松耦合和紧耦合是关于程序组件之间的依赖关系。这意味着不仅依赖于编程类,还依赖于编程系统组件。

例如,如果你只使用简单的原始SQL查询从SQL Server接收数据,这是松耦合。与松耦合和简单原始SQL查询相反的是紧耦合和实体框架核心。在实体框架核心,你必须完整的模型与POCO类在你的代码反映数据库结构,这意味着任何变化在数据库中,你必须反映在代码。

因此,程序代码和数据库结构之间的紧密耦合是实体框架,这种方法的对立面是拒绝使用任何ORM,并拒绝在程序代码中使用完整的镜像数据库结构。