我似乎无法理解“松散耦合”的概念。我想这不利于单词“宽松”通常有负面的含义,所以我总是忘了松耦合是一个很好的事情。
有人可以显示说明此概念的“之前”和“之后”代码(或伪代码)吗?
我似乎无法理解“松散耦合”的概念。我想这不利于单词“宽松”通常有负面的含义,所以我总是忘了松耦合是一个很好的事情。
有人可以显示说明此概念的“之前”和“之后”代码(或伪代码)吗?
Answers:
考虑一个简单的购物车应用程序,该应用程序使用CartContents类来跟踪购物车中的项目,并使用Order类来处理购买。订单需要确定购物车中内容的总价值,它可能会像这样:
紧密耦合示例:
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类的实现细节。如果我们试图更改此逻辑以允许折扣,则可能必须更改所有三个类别。另外,如果我们更改为使用List集合来跟踪商品,则也必须更改Order类。
现在,这是一种执行相同操作的更好方法:
较少耦合的示例:
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);
}
}
特定于购物车订单项,购物车集合或订单的实现的逻辑仅限于该类。因此,我们可以更改任何这些类的实现,而不必更改其他类。我们可以通过改进设计,引入接口等来进一步消除这种耦合,但是我认为您明白这一点。
Order
有所了解Cart
。现场购物和制造是两种不同的情况。当客户准备付款时,您可以有一个流程负责将CartEntries转换成对有意义的东西Order
。这样一来,Order
该类也将被实例化,并且无需使用Cart
。
CartEntry
和CartContents
。由于这些具有完全清晰的含义,因此应将它们简单地建模为无效数据。用数据库的术语来说,这里只需要一个表CREATE TABLE entry (price INTEGER, quantity INTEGER)
iPod,iPad等许多集成产品(尤其是Apple的产品)就是紧密耦合的一个很好的例子:一旦电池耗尽,您最好购买新设备,因为电池已焊接固定并且不会松动,因此更换非常容易昂贵。松散耦合的播放器可以轻松更换电池。
这同样也适用于软件开发:它通常是(多)最好有松耦合代码,方便扩展和替换(并且使各个部件更容易理解)。但是,在极少数情况下,紧密耦合可能是有利的,因为几个模块的紧密集成可以实现更好的优化。
我将以Java为例。假设我们有一个看起来像这样的类:
public class ABC
{
public void doDiskAccess() {...}
}
当我叫课时,我需要做这样的事情:
ABC abc = new ABC();
abc. doDiskAccess();
到目前为止,一切都很好。现在,让我们说另一个类如下:
public class XYZ
{
public void doNetworkAccess() {...}
}
它看起来与ABC完全相同,但是可以说它通过网络而不是磁盘工作。现在让我们编写一个这样的程序:
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不依赖于调用它们的类的任何方法或变量。这使ABC和XYZ成为完全独立的API。或者换句话说,它们是与父类“分离”或“松散耦合”的。
但是,如果我们需要两者之间的沟通怎么办?好了,然后我们可以使用事件模型等其他抽象来确保父代码永远不需要与您创建的API耦合。
DiskAccessor
和,这是否更易读NetworkAccessor
?我不知道没有Java ...
抱歉,“松耦合”不是编码问题,而是设计问题。术语“松散耦合”与“高内聚力”的理想状态紧密相关,相反但互补。
松散耦合只是意味着应该构造各个设计元素,从而减少了他们需要了解的有关其他设计元素的不必要信息。
高内聚性有点像“紧密耦合”,但高内聚性是一种状态,在这种状态下,真正需要彼此了解的设计元素经过精心设计,可以干净,优雅地协同工作。
关键是,某些设计元素应了解其他设计元素的详细信息,因此应以这种方式进行设计,而不是偶然地进行设计。其他设计元素不应该知道其他设计元素的详细信息,因此应该有目的地而不是随机地进行设计。
留给读者练习是实现:)。
紧密耦合的代码依赖于具体的实现。如果我的代码中需要一个字符串列表,并且我这样声明它(在Java中)
ArrayList<String> myList = new ArrayList<String>();
然后我依赖于ArrayList实现。
如果我想将其更改为松散耦合的代码,请使我的引用为接口(或其他抽象)类型。
List<String> myList = new ArrayList<String>();
这样可以防止我调用任何方法myList
特定于ArrayList实现的。我仅限于List接口中定义的那些方法。如果以后再决定确实需要一个LinkedList,则只需要在创建新List的一个地方更改代码,而不是在对ArrayList方法进行调用的100个地方更改代码。
当然,您可以使用第一个声明来实例化ArrayList,并限制自己不要使用不属于List接口的任何方法,但是使用第二个声明会使编译器保持诚实。
此处答案之间的差异程度说明了为什么很难理解这个概念,但将其简单地描述为:
为了让我知道,如果我向您扔一个球,那么您就可以接住球,我真的不需要知道您多大了。我不需要知道您早餐吃了什么,而且我真的不在乎您的第一个恋人是谁。我所需要知道的是,您可以抓住。如果我知道这一点,那么我不在乎它是否是我向您或您的兄弟扔球。
对于非动态语言,例如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。它是硬编码的,因此它一直呆在那里直到有人出现并对其进行重构。
现在让我们采取另一个选项,称为依赖注入:
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框架等。
您可以认为(紧密或松散)耦合实际上是您将特定类从对另一个类的依赖中分离出来所需要付出的努力。例如,如果类中的每个方法在底部调用Log4Net记录某些内容的底部都有一个finally块,那么您会说您的类与Log4Net紧密耦合。如果您的类包含一个名为LogSomething的私有方法,而该方法是唯一一个调用Log4Net组件的地方(其他方法都称为LogSomething),那么您会说您的类与Log4Net松散耦合了(因为它不需要太多时间)努力拉出Log4Net并将其替换为其他东西)。
64BitBob
自己更喜欢答案。
从本质上讲,耦合是给定对象或一组对象完成另一个任务所依赖的对象。
想想一辆车。为了使发动机启动,必须将钥匙插入点火开关中,转动,必须存在汽油,必须产生火花,必须点火活塞,并且发动机必须带电。您可以说汽车发动机与其他几个物体高度耦合。这是高耦合,但这并不是一件坏事。
想想一个用于网页的用户控件,该控件负责允许用户发布,编辑和查看某些类型的信息。单个控件可用于让用户发布新信息或编辑新信息。该控件应该能够在两个不同的路径之间共享-新建和编辑。如果控件的编写方式使得它需要包含它的页面中的某种类型的数据,那么您可以说它耦合度太高。该控件应该从其容器页面不需要任何内容。
这是一个非常笼统的概念,因此代码示例不会给出完整的图片。
在这里工作的一个人对我说:“图案就像分形,当您放大得很近时,以及缩小到建筑级别时,您都可以看到它们。”
阅读简短的维基百科页面可以使您大致了解一下:
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是我打算在此处进行的植入。
如果愿意,我可以将其提取出来并替换为XYZCustomizableGridService或StubCustomizableGridService,而对该依赖项的类完全没有更改。
如果我直接引用了ABCCustomizableGridService,则需要对该引用进行更改,以便交换另一个服务实现。
耦合与系统之间的依赖关系有关,这些依赖关系可以是代码模块(函数,文件或类),管道中的工具,服务器-客户端进程等。依赖性越不通用,它们就越“紧密耦合”,因为更改一个系统需要更改依赖它的其他系统。理想的情况是“松耦合”,其中一个系统可以更改,并且依赖该系统的系统将继续工作而无需修改。
实现松散耦合的一般方法是通过定义明确的接口。如果两个系统之间的交互作用得到很好的定义,并且双方都坚持,那么在确保不破坏约定的同时修改一个系统变得更加容易。在实践中通常会发生以下情况:没有建立明确定义的接口,从而导致设计草率和紧密耦合。
一些例子:
应用程序取决于库。在紧密耦合下,应用程序在较新版本的lib上中断。Google的“ DLL Hell”。
客户端应用程序从服务器读取数据。在紧密耦合下,对服务器的更改需要在客户端进行修复。
两个类在面向对象的层次结构中进行交互。在紧密耦合下,对一个类别的更改要求将另一个类别更新以匹配。
多个命令行工具在管道中进行通信。如果它们紧密耦合,则更改一个命令行工具的版本将导致读取其输出的工具出错。
耦合是指不同的类之间相互连接的紧密程度。紧密耦合的类包含大量的交互和依赖性。
松散耦合的类是相反的,因为它们彼此之间的依赖关系被最小化,而是相互依赖定义良好的公共接口。
乐高玩具,即SNAP在一起的玩具,可以认为是松散耦合的,因为您可以将零件拼合在一起,并构建所需的任何系统。但是,拼图游戏的零件紧密相连。您不能从一个拼图游戏(系统)中提取一个片段并将其捕捉到另一个拼图中,因为系统(拼图)非常依赖于针对特定“设计”而构建的非常具体的片段。乐高积木以更通用的方式构建,因此可以在您的Lego House或我的Lego Alien Man中使用。
参考:https : //megocode3.wordpress.com/2008/02/14/coupling-and-cohesion/
当两个组件依赖于彼此的具体实现时,它们是高耦合的。
假设我的代码在类的某个方法中的某处:
this.some_object = new SomeObject();
现在我的课依赖于SomeObject,并且它们之间是高度耦合的。另一方面,假设我有一个InjectSomeObject方法:
void InjectSomeObject(ISomeObject so) { // note we require an interface, not concrete implementation
this.some_object = so;
}
然后,第一个示例可以只使用注入的SomeObject。这在测试期间很有用。在正常操作下,您可以使用繁重的数据库使用类和网络使用类等,而对于通过轻量级模拟实现的测试。使用紧密耦合的代码,您将无法做到这一点。
您可以通过使用依赖项注入容器来简化此工作的某些部分。您可以在Wikipedia上了解有关DI的更多信息:http : //en.wikipedia.org/wiki/Dependency_injection。
有时很容易将其推到太远。在某些时候,您必须使事情具体化,否则程序的可读性和可理解性就会降低。因此,主要在组件边界使用此技术,并了解您的工作。确保您利用的是松散耦合。如果没有,您可能在那个地方不需要它。DI可能会使您的程序更复杂。确保您进行了良好的权衡。换句话说,保持良好的平衡。与设计系统时一样。祝好运!
考虑带有FormA和FormB的Windows应用程序。FormA是主要表单,它显示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只能有FormA类型的父对象。
另一方面,如果您让FormB发布事件并让FormA订阅该事件,则FormB可以将数据通过该事件推回该事件拥有的任何订户。然后,在这种情况下,FormB甚至不知道其与父级的对话。通过松散耦合,事件提供了与订阅者的简单对话。现在,任何类型都可以成为FormA的父级。
rp
在计算机科学中,“松散耦合”还有另一种含义,这是其他人都没有发表的意思,所以……来吧-希望您能给我一些票,以免这没错。当然,我的回答的主题属于对该问题的任何全面回答。
术语“松散耦合”首先进入计算领域,该术语用作有关多CPU配置中CPU体系结构的形容词。它的对应术语是“紧密耦合”。松散耦合是指CPU不共享许多资源,而紧密耦合是指CPU共享时。
“系统”一词在这里可能令人困惑,因此请仔细分析情况。
通常但并非总是如此,在一个系统中(如在单独的“ PC”盒中)它们存在的硬件配置中的多个CPU将紧密耦合。除了某些超高性能系统具有子系统实际上跨“系统”共享主内存的系统以外,所有可分割的系统都是松散耦合的。
在发明多线程和多核CPU 之前,先引入了紧密耦合和松散耦合这两个术语,因此这些术语可能需要一些同伴来全面阐述当今的情况。而且,的确,今天,很可能拥有一种在一个整体系统中同时包含两种类型的系统。对于当前的软件系统,有两种通用的体系结构,每种体系中的一种,它们足够通用,应该很熟悉。
首先,既然是问题所在,那么松耦合系统的一些示例:
相反,一些紧密耦合的示例:
在当今的计算中,两者在单个整体系统中运行的例子并不罕见。例如,以运行Fedora 9的现代奔腾双核或四核CPU-这些是紧密耦合的计算系统。然后,将其中的几个结合到一个松散耦合的Linux集群中,现在您将同时进行松散耦合和紧密耦合的计算!哦,现代硬件不是很棒!
用简单的语言来说,松散耦合意味着它不依赖于其他事件的发生。它独立执行。
我提出了一个非常简单的代码耦合测试:
如果对代码段B进行了任何可能的修改,这些代码段将迫使代码段A进行更改以保持正确性,则代码段A与代码段B紧密耦合。
如果没有对代码段B进行任何可能需要更改代码段A的修改,则代码段A与代码段B紧密耦合。
这将帮助您验证代码段之间有多少耦合。有关其推理的信息,请参阅此博客文章:http : //marekdec.wordpress.com/2012/11/14/loose-coupling-tight-coupling-decoupling-what-is-that-all-about/
当您new
在其他类中使用关键字创建类的对象时,实际上是在进行紧密耦合(不好的做法),而应该使用松散耦合,这是一个好习惯
--- 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");
}
}
-接口类-
package interface_package.loose_coupling;
public interface InterfaceClass {
void display();
String getVar();
}
---主类-
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类定义了一个Contract,因为InterfaceClass具有B类的抽象方法,可以由任何其他类(例如A)访问。
在A类中,我们有一种显示方法,该方法可以除实现InterfaceClass的类对象(在我们的情况下为B类)之外。在该对象上,类A的方法正在调用类B的display()和getVar()
在MainClass中,我们创建了A类和B类的对象。并通过传递B类的对象objb来调用A的显示方法。A的显示方法将与B类的对象一起调用。
现在谈论松散耦合。假设将来您必须将B类的名称更改为ABC,那么就不必在B类的显示方法中更改其名称,只需创建new对象(ABC类)并将其传递给MailClass中的显示方法即可。您无需在A类中进行任何更改
参考:http://p3lang.com/2013/06/loose-coupling-example-using-interface/
通常,松散耦合是2个参与者在相同的工作负载上彼此独立工作。因此,如果您有2个Web服务器使用相同的后端数据库,那么您会说这些Web服务器是松散耦合的。一台Web服务器上有2个处理器,可以举例说明紧密耦合。这些处理器紧密耦合。
希望这会有所帮助。