Answers:
紧密耦合是一组类彼此高度依赖时。
当班级承担太多职责时,或者当一个关注点分散到多个班级而不是拥有自己的班级时,就会出现这种情况。
松耦合是通过促进单一责任和关注点分离的设计实现的。
松散耦合的类可以独立于其他(具体)类使用和测试。
接口是用于解耦的强大工具。类可以通过接口而不是其他具体类进行通信,并且任何类都可以通过实现接口而位于通信的另一端。
紧密耦合的示例:
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)
{
}
}
这里的另一个例子。
帽子与身体“松散地耦合”。这意味着您可以轻松脱下帽子,而无需对人/身体进行任何更改。如果可以做到这一点,那么您将拥有“松散耦合”。详见下文。
想想你的皮肤。它粘在你的身上。它像手套一样合身。但是,如果您想将肤色从白色变为绿色怎么办?您能想象脱皮,染色,再粘贴回去会多么痛苦吗?更换皮肤很困难,因为它与您的身体紧密相连。您只是无法轻松进行更改。您必须从根本上重新设计人才能使之成为可能。
上帝不是一个好的面向对象的程序员。
现在想想早上穿衣服。你不喜欢蓝色吗?没问题:您可以穿一件红色的衬衫代替。您可以轻松而轻松地完成此操作,因为衬衫与皮肤的连接方式实际上与您的身体没有真正的连接。这件衬衫不知道也不在乎它在干什么。换句话说,您可以在不真正改变身体的情况下更换衣服。
简而言之,这就是基本概念。
这很重要,因为软件一直在变化。一般来说,您希望能够轻松修改代码而不更改代码。我知道这听起来很矛盾,但请多多包涵。
CSV / JSON / DB示例:如果有人希望将其输出以CSV文件而不是JSON等格式存储,或者如果您想从MySQL切换到PostGreSQL,则应该能够非常轻松地在代码中进行这些更改,而无需重写换句话说,您不想将应用程序与特定的数据库实现(例如Mysql)或特定的输出(例如CSV文件)紧密耦合。因为,正如软件中不可避免的那样,更改将到来。当它们出现时,如果您的代码部分松散耦合,则容易得多。
汽车零件示例:如果有人想要用黑色驾驶汽车,那么您不必为此重新设计整个汽车。汽车及其零配件将是松散耦合架构的完美示例。如果您想用更好的引擎更换引擎,则应该能够轻松卸下引擎,而将其更换为更好的引擎。如果您的汽车仅适用于劳斯莱斯1234发动机,而不适用于其他发动机,那么您的汽车将与该发动机紧密相连(劳斯莱斯1234)。如果您更改汽车的设计,使其与任何汽车兼容,那会更好。引擎,使其与组件之间的连接更加松散。如果您的汽车完全不需要引擎就可以工作,那就更好了!某种程度的耦合将要发生,但您应努力将其最小化。为什么?因为当需求发生变化时,我们仍然应该能够非常快速地交付高质量的软件,并且通过松耦合可以帮助我们实现该目标。
简而言之,松散的耦合使代码更易于更改。上面的答案提供了一些此时值得阅读的代码。
回复:@TimoHuovinen评论-松耦合的概念与多态性紧密相关。如果您掌握了衬衫/汽车部件的基本类比,那么您将可以理解多态性。最好的方法是,在此线程的其他答案中,阅读我可估计的同事提供的代码示例。如果我再说一遍,您可能会得到太多信息。
图片归因。
在面向对象的设计中,耦合的数量是指一个类的设计在多大程度上取决于另一类的设计。换句话说,A级的变化多久与B级的力变化有关?紧耦合意味着这两个类别经常一起改变,松耦合意味着它们大多是独立的。通常,建议使用松散耦合,因为它更易于测试和维护。
您可能会发现Martin Fowler(PDF)的这篇论文很有帮助。
通常,紧密耦合在很多时候是不好的,但是在大多数情况下,因为它降低了代码的灵活性和可重用性,使更改变得更加困难,阻碍了可测试性等。
紧密耦合对象是一个需要彼此了解很多的对象,并且通常高度依赖于彼此的接口。在紧密耦合的应用程序中更改一个对象通常需要对许多其他对象进行更改。在小型应用程序中,我们可以轻松地识别出更改,而错过任何事物的机会则更少。但是在大型应用程序中,并不是每个程序员都知道这些相互依存关系,否则就有机会错过更改。但是,每组松散耦合的对象都不依赖于其他对象。
简而言之,松耦合是一个设计目标,旨在减少系统组件之间的相互依赖性,以降低一个组件中的更改将需要更改任何其他组件的风险。松散耦合是一个更为通用的概念,旨在提高系统的灵活性,使其更易于维护,并使整个框架更“稳定”。
耦合是指一个元素具有另一元素的直接知识程度。我们可以说一个例子:A和B,只有当A改变其行为时,只有B改变其行为。松散耦合的系统很容易分解为可定义的元素。
我关于耦合的博客文章的摘录:
什么是紧耦合:
如上定义,紧密耦合对象是需要了解其他对象的对象,通常高度依赖于彼此的接口。
当我们在紧密耦合的应用程序中更改一个对象时,通常需要更改许多其他对象。在小型应用程序中没有问题,我们可以轻松识别更改。但是,在大型应用程序中,并非每个消费者或其他开发人员都知道这些相互依存关系,否则将来会有很多改变的机会。
让我们以购物车演示代码来了解紧密耦合:
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()
方法为我们提供了购物车中当前物品的完整金额。如果我们要在此购物车系统中添加折扣功能。在上面的代码中很难做到这一点,因为我们必须在每个类上进行更改,因为它们之间的耦合非常紧密。
据我了解,与松散耦合的体系结构相比,紧密耦合的体系结构不能为更改提供很大的灵活性。
但是在松散耦合的体系结构,消息格式或操作平台或修改业务逻辑的情况下,不会影响另一端。如果关闭系统进行改造,那么另一端当然将不能再访问该服务一段时间,但除此之外,未更改的另一端可以像进行改造之前那样恢复消息交换。
紧密耦合意味着一个类别依赖于另一个类别。
松耦合意味着一类依赖于接口而不是类。
在紧密耦合中,方法中声明了硬编码的依赖项。
在松耦合中,我们必须在运行时从外部传递依赖关系,而不是硬编码。(松耦合系统使用接口来减少对类的依赖。)
例如,我们有一个系统可以通过两种或多种方式发送输出,例如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();
}
}
有某些工具可以通过其库提供依赖项注入,例如,在.net中,我们有ninject Library。
如果要进一步使用Java,则spring提供了此功能。
松耦合的对象可以通过在代码中引入接口来实现,这就是这些源代码所做的。
在您的代码中说您正在编写
Myclass m = new Myclass();
现在,您的方法中的此语句说您依赖myclass
于此,称为紧密耦合。现在,您提供了一些构造函数注入,或属性注入和实例化对象,那么它将变得松散耦合。
这里使用类比有很多不错的答案,但是工作中的一位朋友给了我一个例子,我比这里提到的所有例子都更喜欢……眼睛和眼镜!
紧耦合
紧密的耦合将是眼睛。如果我想固定视力,则进行眼科手术的费用并不昂贵,而且存在很大的风险。但是,如果设计师(人类)找到了更好的方法,该怎么办。添加与身体松散耦合的功能,以便可以轻松更改!(是..眼镜)
松耦合
我可以轻松更换眼镜,而又不会破坏我的基本视野。我可以摘下眼镜,而我的目光将是以前的样子(不坏不坏)。使用不同的眼镜会改变我们通过眼睛观察世界的方式,风险很小且易于维护。
摘要
因此,下次有人问您“谁在乎我的代码是否紧密耦合?” 答案全在于变革的努力,维护的努力和变革的风险。
那么如何在C#中完成呢?接口和依赖注入!
编辑
这也是装饰器模式的一个很好的例子,通过满足界面要求,眼睛却是我们正在装饰的类,但是具有不同的功能(例如太阳镜,老花镜,珠宝商的放大镜等)
它是关于另一个依赖的类的依赖率,它的松散耦合性很低,而紧密耦合性很高。要在明确的面向服务架构,服务是松散耦合的,彼此对单片哪些类依赖于对方是故意的
如果对象的创建/存在依赖于无法定制的另一个对象,则其紧密耦合。并且,如果可以定制依赖项,则其松散耦合。考虑一下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)在接受的答案中的评论有很好的解释。
紧密耦合意味着类和对象相互依赖。通常,紧密耦合通常不好,因为它会降低代码的灵活性和可重用性,而松散耦合意味着减少直接使用不同类的类的依赖关系。
紧密耦合紧密耦合的对象是需要了解其他对象的对象,通常高度依赖于彼此的接口。在紧密耦合的应用程序中更改一个对象通常需要更改许多其他对象。在小型应用程序中,我们可以轻松识别更改,并且错过任何内容的机会都更少。但是在大型应用程序中,并不是每个程序员都知道这些相互依存关系,因此有可能忽略更改。例:
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