什么是“松耦合”?请举例说明

我似乎无法理解“松耦合”的概念。 我认为“松散”这个词通常不具有负面的含义,所以我总是忘记,松耦合是一件好事。

有人会请示出一些说明这个概念的“之前”和“之后”代码(或伪代码)吗?

考虑一个简单的购物车应用程序,该应用程序使用CartContents类来跟踪购物车中的物品和用于处理购买的订单类。 订单需要确定购物车中的内容的总价值,可能是这样的:

紧密耦合示例:

public class CartEntry { public float Price; public int Quantity; } public class CartContents { public CartEntry[] items; } public class Order { private CartContents cart; private float salesTax; public Order(CartContents cart, float salesTax) { this.cart = cart; this.salesTax = salesTax; } public float OrderTotal() { float cartTotal = 0; for (int i = 0; i < cart.items.Length; i++) { cartTotal += cart.items[i].Price * cart.items[i].Quantity; } cartTotal += cartTotal*salesTax; return cartTotal; } } 

请注意OrderTotal方法(以及Order类)如何取决于CartContents和CartEntry类的实现细节。 如果我们试图改变这个逻辑来考虑折扣,我们可能不得不改变所有3个class级。 此外,如果我们改变使用List集合来跟踪项目,我们也必须改变Order类。

现在这里有一个更好的方法来做同样的事情:

较less耦合的例子:

 public class CartEntry { public float Price; public int Quantity; public float GetLineItemTotal() { return Price * Quantity; } } public class CartContents { public CartEntry[] items; public float GetCartItemsTotal() { float cartTotal = 0; foreach (CartEntry item in items) { cartTotal += item.GetLineItemTotal(); } return cartTotal; } } public class Order { private CartContents cart; private float salesTax; public Order(CartContents cart, float salesTax) { this.cart = cart; this.salesTax = salesTax; } public float OrderTotal() { return cart.GetCartItemsTotal() * (1.0f + salesTax); } } 

特定于购物车行项目或购物车集合或订单的逻辑仅限于该类别。 所以我们可以改变任何这些类的实现而不必改变其他的类。 我们可以通过改进devise,引入接口等来进一步解耦,但是我认为你明白了。

iPod是紧密耦合的一个很好的例子:一旦电池耗尽,你不妨买一个新的iPod,因为电池焊接固定,不会松动,因此使得更换非常昂贵。 松耦合的播放器可以毫不费力地更换电池。

1:1也是一样的软件开发。

我将以Java为例。 假设我们有一个如下所示的类:

 public class ABC { public void doDiskAccess() {...} } 

当我打电话给class级时,我需要做这样的事情:

 ABC abc = new ABC(); abc. doDiskAccess(); 

到现在为止还挺好。 现在让我们说我有另一个类,看起来像这样:

 public class XYZ { public void doNetworkAccess() {...} } 

它看起来和ABC完全一样,但是让我们说它在networking上而不是在磁盘上。 现在我们来编写一个这样的程序:

 if(config.isNetwork()) new XYZ().doNetworkAccess(); else new ABC().doDiskAccess(); 

这有效,但有点笨拙。 我可以用这样的接口来简化它:

 public interface Runnable { public void run(); } public class ABC implements Runnable { public void run() {...} } public class XYZ implements Runnable { public void run() {...} } 

现在我的代码可以看起来像这样:

 Runnable obj = config.isNetwork() ? new XYZ() : new ABC(); obj.run(); 

看看有多清楚和简单的理解是什么? 我们刚刚了解了松耦合的第一个基本原理:抽象。 这里的关键是确保ABC和XYZ不依赖于调用它们的类的任何方法或variables。 这使ABC和XYZ成为完全独立的API。 换句话说,它们与父类“分离”或“松散耦合”。

但是如果我们需要两者之间的沟通呢? 那么,我们可以使用更多的抽象,如事件模型,以确保父代码永远不需要与您创build的API耦合。

对不起,但“松耦合”不是一个编码问题,这是一个devise问题。 “松耦合”一词与“高内聚”的理想状态密切相关,相反但互补。

松耦合仅仅意味着应该构build单独的devise元素,以减less他们需要了解的其他devise元素的不必要信息的数量。

高凝聚力就像“紧密结合”,但高凝聚力是一个真正需要了解彼此的devise元素是如何devise的,使他们能够干净而优雅地合作。

重点是,一些devise元素应该知道其他devise元素的细节,所以他们应该这样devise,而不是偶然。 其他devise元素不应该知道其他devise元素的细节,所以应该这样devise,有目的地,而不是随机的。

实现这个是留给读者的一个练习:)。

紧密耦合的代码依赖于具体的实现。 如果我需要在我的代码中的string列表,我声明这样(在Java中)

 ArrayList<String> myList = new ArrayList<String>(); 

那么我依赖于ArrayList实现。

如果我想改变松散耦合的代码,我把我的引用一个接口(或其他抽象)types。

 List<String> myList = new ArrayList<String>(); 

这可以防止我调用myList上特定于ArrayList实现的任何方法。 我仅限于在List接口中定义的那些方法。 如果我以后决定真的需要一个LinkedList,我只需要在一个地方更改我的代码,在那里创build新的List,而不是在我调用ArrayList方法的地方。

当然,你可以使用第一个声明实例化一个ArrayList,并限制自己不使用任何不属于List接口的方法,但是使用第二个声明使编译器保持诚实。

你可以把(紧密的或松散的)耦合看作是将一个特定的类别与另一个类别的依赖区分开来所花费的精力。 例如,如果你的类中的每个方法都有一点阻塞在你调用Log4Netlogging的底部,那么你会说你的类和Log4Net是紧密耦合的。 如果你的类包含一个名为LogSomething的私有方法,这个方法是唯一调用Log4Net组件的方法(而其他方法全部调用LogSomething),那么你会说你的类与Log4Net松散耦合(因为它不会花费太多努力拉出Log4Net,并用其他东西replace)。

这里的答案之间的差异程度说明了为什么这将是一个困难的概念要把握,但只要我能形容它:

为了让我知道,如果我把球扔给你,那么你可以抓住它,我真的不需要知道你是多大年纪。 我不需要知道你早餐吃什么,我真的不关心你的第一次暗恋是谁。 我需要知道的是你可以抓到。 如果我知道这一点,那么我不在乎,如果你我把球扔给你或你的兄弟。

使用非dynamic语言(如c#或Java等),我们通过Interfaces来完成。 所以可以说我们有以下的接口:

 public ICatcher { public void Catch(); } 

现在让我们说,我们有以下类:

 public CatcherA : ICatcher { public void Catch() { console.writeline("You Caught it"); } } public CatcherB : ICatcher { public void Catch() { console.writeline("Your brother Caught it"); } } 

现在CatcherA和CatcherB都实现了Catch方法,所以需要Catcher的服务可以使用其中的任何一个,而不是真正地给出它是哪一个。 所以一个紧密耦合的服务可能会直接实现一个捕获的即服务

 public CatchService { private CatcherA catcher = new CatcherA(); public void CatchService() { catcher.Catch(); } } 

因此,CatchService可以完成它所要做的事情,但它使用CatcherA,并且将始终使用CatcherA。 它的硬编码,所以它呆在那里,直到有人来,并重构它。

现在让我们看看另一个选项,称为dependency injection:

 public CatchService { private ICatcher catcher; public void CatchService(ICatcher catcher) { this.catcher = catcher; catcher.Catch(); } } 

因此,CatchService的实例可能会做到以下几点:

 CatchService catchService = new CatchService(new CatcherA()); 

要么

 CatchService catchService = new CatchService(new CatcherB()); 

这意味着Catch服务与CatcherA或CatcherB没有紧密耦合。

还有其他的一些松散耦合服务,比如使用IoC框架等。

定义

本质上,耦合是一个给定的对象或一组对象依赖另一个对象或另一组对象来完成它的任务。

高耦合

想想汽车。 为了使发动机起动,必须将钥匙插入点火装置,转动,必须存在汽油,必须发生火花,活塞必须点火,发动机必须活着。 你可以说汽车发动机与其他几个物体高度耦合。 这是高耦合,但这不是一件坏事。

松耦合

想想一个网页的用户控件,它负责允许用户发布,编辑和查看某种types的信息。 单个控件可以用来让用户发布一条新的信息或编辑一条新的信息。 控件应该能够在两个不同的path之间共享 – 新build和编辑。 如果控件的写入方式需要来自页面的某种数据,那么你可以说它是高度耦合的。 该控件不应该在其容器页面上需要任何东西。

这是一个相当普遍的概念,所以代码示例不会给出整个图像。

这里有一位工作人员对我说:“图案就像分形,当你放大的时候你可以看到它们,当你放大到架构层次的时候,就可以看到它们。

阅读简短的维基百科页面可以让你了解这个普遍性:

http://en.wikipedia.org/wiki/Loose_coupling

至于特定的代码示例…

这是我最近使用的一个松耦合,从Microsoft.Practices.CompositeUI的东西。

  [ServiceDependency] public ICustomizableGridService CustomizableGridService { protected get { return _customizableGridService; } set { _customizableGridService = value; } } 

此代码声明此类对CustomizableGridService具有依赖关系。 它不仅仅是直接引用服务的确切实现,而是简单地指出它需要某种服务的实现。 然后在运行时,系统解决这个依赖关系。

如果不清楚,可以在这里阅读更详细的解释:

http://en.wikipedia.org/wiki/Dependency_injection

想象一下,ABCCustomizableGridService是我打算在这里挂钩的imlpementation。

如果我select,我可以将它取出来,用XYZCustomizableGridService或StubCustomizableGridServicereplace它,根本不需要更改。

如果我直接引用了ABCCustomizableGridService,那么我需要对那些/这些引用/ s进行更改,以便交换另一个服务实现。

耦合与系统之间的依赖性有关,可能是代码模块(函数,文件或类),pipe道中的工具,服务器 – 客户端进程等等。 依赖关系越一般,它们变得越“紧密结合”,因为改变一个系统需要改变依赖它的其他系统。 理想的情况是“松耦合”,一个系统可以改变,而依赖它的系统将继续工作而不用修改。

实现松耦合的一般方法是通过定义良好的接口。 如果两个系统之间的交互作用得到了很好的界定,并且在双方都保持一致,那么修改一个系统变得更容易,同时确保这些惯例不被破坏。 在实践中通常会发生没有明确定义的界面,导致devise草率和紧密耦合。

一些例子:

  • 应用程序依赖于库。 在紧密耦合下,应用程序在新版本的lib中断。 谷歌的“DLL地狱”。

  • 客户端应用程序从服务器读取数据。 在紧密耦合下,对服务器的更改需要客户端上的修复。

  • 两个类在面向对象的层次结构中进行交互。 在紧密耦合下,更改一个类需要更新其他类才能匹配。

  • 多个命令行工具在pipe道中进行通信。 如果它们紧密耦合,那么更改一个命令行工具的版本将导致读取其输出的工具发生错误。

用简单的语言,松耦合意味着它不依赖于其他事件发生。 它独立执行。

考虑与FormA和FormB的Windows应用程序。 FormA是主要的forms,它显示FormB。 想象一下FormB需要将数据传回给它的父代。

如果你这样做:

 class FormA { FormB fb = new FormB( this ); ... fb.Show(); } class FormB { FormA parent; public FormB( FormA parent ) { this.parent = parent; } } 

FormB与FormA紧密耦合。 FormB除了FormAtypes之外,不能有其他的父类。

另一方面,如果您有FormB发布事件并让FormA订阅该事件,则FormB可以通过该事件将数据推送回该事件所具有的任何订阅者。 在这种情况下,FormB甚至不知道它与其父母的谈话。 通过松耦合,事件提供了简单的与用户交谈。 现在任何types都可以成为FormA的父类。

RP

两个组件在相互依靠具体实现时是高度耦合的。

假设我在我的类的某个方法中有这个代码:

 this.some_object = new SomeObject(); 

现在我的类依赖于SomeObject,并且它们高度耦合。 另一方面,比方说,我有一个方法InjectSomeObject:

 void InjectSomeObject(ISomeObject so) { // note we require an interface, not concrete implementation this.some_object = so; } 

那么第一个例子可以使用注入的SomeObject。 这在testing过程中很有用。 在正常的操作中,您可以使用大量使用数据库的networking使用类等,而testing通过轻量级的模拟实现。 紧密耦合的代码,你不能这样做。

通过使用dependency injection容器,可以使这个工作的某些部分更加容易。 您可以在Wikipedia上阅读更多关于DI的内容: http : //en.wikipedia.org/wiki/Dependency_injection 。

有时候这太容易了。 在某些时候,你必须做出具体的事情,否则你的程序会变得不可读和易于理解。 所以主要在组件边界使用这种技术,并知道你在做什么。 确保你正在利用松耦合。 如果不是的话,你可能不需要那个地方。 DI可能会让你的程序更复杂。 确保你做一个好的折衷。 换句话说,保持良好的平衡。 一如既往地devise系统。 祝你好运!

在计算机科学中,还有其他人没有在这里发表的关于“松散耦合”的另一个含义,所以…希望你能给我一些票,所以这不会在堆的底部丢失! 对于这个问题,我的回答很显然是全面的回答。

术语“松耦合”首先作为在多CPUconfiguration中作为关于CPU架构的形容词使用的术语进行计算。 其对应的术语是“紧密耦合”。 松耦合是CPU不共享许多共享资源的时候,紧耦合是当他们这样做的时候。

“系统”这个词在这里可能会令人困惑,所以请仔细分析一下这个情况。

通常(但不总是),在一个系统中存在的硬件configuration中的多个CPU(如在个人“PC”框中)将紧密耦合。 除了一些超系统具有实际上在“系统”上共享主存的子系统之外,所有可分割的系统都是松散耦合的。

multithreading和多核CPU发明之前 ,引入了紧密耦合和松散耦合的术语,所以这些术语可能需要一些同伴来充分阐述当前的情况。 而且,的确,今天人们可能拥有一个能够在一个整体系统中实现两种types的系统。 关于当前的软件系统,有两种常见的架构,每种架构都是常见的,这些架构应该是家喻户晓的。

首先,由于这是问题所在,松散耦合系统的一些例子:

  • VaxClusters
  • Linux集群

相反,一些紧密结合的例子:

  • 分步多处理(SMP)操作系统 – 例如Fedora 9
  • multithreading的CPU
  • 多核CPU

在今天的计算中,在单个整体系统中运行的例子并不less见。 例如,采用运行Fedora 9的现代奔腾双核或四核CPU,这些都是紧密耦合的计算系统。 然后,将它们中的几个组合在一个松散耦合的Linux集群中,您现在既有松耦合又紧密耦合的计算。 哦,现代硬件不是太棒了!

一些长的答案在这里。 原则是非常简单的。 我提交维基百科的开头语句:

“松耦合描述了两个或多个系统或组织之间具有某种交换关系的弹性关系。

交易的每一端都要求明确的要求,并且对另一端做出很less的假设。“

我提出了一个非常简单的代码耦合testing

  1. 如果存在对片断B的任何可能的修改,为了保持正确性将迫使片断A中的改变,片断A的代码紧密地耦合到代码片断B.

  2. 如果没有可能对P片B进行修改以使P片A有必要改变的情况,则片A的代码不紧密地耦合到片B的代码。

这将帮助你validation你的代码之间有多less耦合。 推理,看到这个博客文章: http : //marekdec.wordpress.com/2012/11/14/loose-coupling-tight-coupling-decoupling-what-is-that-all-about/

耦合是指不同的类彼此连接的程度。 紧密耦合的类包含大量的交互和依赖关系。

松散耦合的类是相反的,因为它们之间的相互依赖性被保持在最低限度,而是依赖于定义明确的公共接口。

乐高玩具,SNAP在一起的玩具将被认为是松散耦合的,因为你可以把它们拼在一起,build立任何你想要的系统。 但是,拼图游戏的拼图块紧密相连。 你不能从一个七巧板(系统)中拿出一块,并把它拼成一个不同的拼图,因为系统(拼图)非常依赖于特定的“特定”devise特定的部分。 这些乐曲是以更加通用的方式build造的,以便它们可以用于您的乐高音乐厅或我的乐高外星人。

参考: https : //megocode3.wordpress.com/2008/02/14/coupling-and-cohesion/

您可以阅读更多关于“松耦合”的通用概念。

简而言之,它描述了两个类之间的关系,其中每个类都至less知道另一个类,每个类可能继续工作,只要其他类是否存在,并且不依赖于另一个类的特定实现类。

当你在其他类中使用new关键字创build一个类的对象时,你实际上是在紧密耦合(不好的练习),而应该使用松耦合,这是一个很好的练习

— A.java —

 package interface_package.loose_coupling; public class A { void display(InterfaceClass obji) { obji.display(); System.out.println(obji.getVar()); } } 

— B.java —

 package interface_package.loose_coupling; public class B implements InterfaceClass{ private String var="variable Interface"; public String getVar() { return var; } public void setVar(String var) { this.var = var; } @Override public void display() { // TODO Auto-generated method stub System.out.println("Display Method Called"); } } 

— InterfaceClass —

 package interface_package.loose_coupling; public interface InterfaceClass { void display(); String getVar(); } 

— MainClass —

 package interface_package.loose_coupling; public class MainClass { public static void main(String[] args) { // TODO Auto-generated method stub A obja=new A(); B objb=new B(); obja.display(objb); //Calling display of A class with object of B class } } 

说明:

在上面的例子中,我们有两个类A和B.

B类实现接口即InterfaceClass。

InterfaceClass定义了一个B类的契约,因为InterfaceClass具有可以被任何其他类访问的B类的抽象方法,例如A.

在A类中,我们有显示方法,可以实现InterfaceClass类的对象(在我们的例子中是B类)。 而在这个对象上,类A的方法是调用类B的display()和getVar()

在MainClass中,我们创build了A类和B类的对象,并通过传递B类的对象objb来调用A的显示方法。 A的显示方法将被调用B类的对象。

现在谈论松耦合。 假设将来您必须将B类的名称更改为ABC,那么您不必在B类的显示方法中更改其名称,只需创build新的(ABC类)对象并将其传递给MailClass中的显示方法即可。 在A类中你不需要改变任何东西

ref: http : //p3lang.com/2013/06/loose-coupling-example-using-interface/

也许最好的比喻是婚姻。

当你还没有结婚的时候,你是松散的。

你可以更容易的离开你的伴侣。

当你“结婚”的时候,你是紧密相连的。

例如,在一些国家,当你离开你的伴侣时,你必须支付赡养费。

一般来说,松耦合是两个参与者在相同的工作负载下彼此独立工作。 所以,如果你有2个Web服务器使用相同的后端数据库,那么你会说这些Web服务器是松耦合的。 紧耦合将通过在一个Web服务器上安装2个处理器来实现……这些处理器紧密耦合。

希望这有些帮助。

Interesting Posts