6 min read

(For more resources on Microsoft products, see here.)

Coupling is the degree to which two things are related. Coupling and cohesion go hand in hand. Something that is highly-cohesive generally has low coupling. Coupling is a form of dependency.

There are many different types of coupling when we’re talking about software design and development. The effort here isn’t to make a decoupled design, it’s to change the coupling. At some level, one piece of code is coupled to every other piece of code in the system—there is rarely any change you can make to change that. Some code is more highly-coupled to other code and uses the other code directly. Some code is more loosely-coupled and uses other code indirectly. Efforts at refactoring towards a more loosely-coupled design are about the degree to which coupling has been made indirect.

Code can be coupled to other code by a shared data format (external coupling). Code can be coupled to other code by the fact that it results in the execution of other code (control coupling). Code can be coupled to other code by the fact that it results in executing other code by way of an abstract interface ( message coupling). Code can be coupled to other code by the fact that they share data, usually in the form of parameters (data coupling ). Code can be coupled to other code by the fact that it has a subclass relationship ( subclass coupling). Code can also be coupled to other code by that fact that it directly references a type’s public interface (content coupling ). The direction and degree to which a type is coupled can also help focus our refactoring efforts. Afferent coupling is the degree to which a type is depended upon (inward coupling). Efferent coupling is the degree to which a type depends on other types (outward coupling ). High afferent coupling can indicate that a type has too many responsibilities. It’s trying to be everything to everyone and thus being used by everyone. High efferent coupling could indicate a type is very dependant. This becomes an issue when the types the class depends upon are in many different assemblies, suggesting a cohesion issue at the assembly layer.

Highly-coupled software is generally accepted to exhibit certain traits, and it can be hard to change. It’s like pulling on the thread of a sweater; there are so many dependencies it’s impossible to predict how much code will need to be modified in order to make a seemingly simple change. Highly-coupled code is also very rigid. It’s hard to move or hard not to duplicate it outside its current context. It carries a lot of baggage (dependencies) with it that need to move with it as a unit. It’s one thing to move the code you want to move, it’s exponentially harder to move all its dependencies.

While good object-oriented design often promotes high cohesion, loosely coupled design and structure can easily fall to the wayside.

Refactoring subclass coupling

Refactoring subclass coupling involves removing the inheritance relationship between two classes.

Refactoring content coupling

Content coupling is one class directly referencing another. There are several tactics for refactoring away this coupling. Depending on the situation, one tactic may be more applicable than another. One tactic is to use interface-based design and remove the coupling to the content of the class and replace it with coupling to an interface that the other class now implements. Another tactic is to replace method calls into the other class with delegate invocations. A final tactic is to use events instead of direct method calls.

For any particular refactoring, a combination of these tactics may be the best solution. You may find that despite a one-to-one coupling between two classes, it’s more appropriate to use a combination of tactics to refactor away the content coupling.

Interface-based design

If you’re already coupled to a particular class, replacing use of that class with an interface and having the other class implement that interface is the easiest way to change the coupling between two classes. This reduces coupling from content coupling to a more loosely coupled message coupling.

If the requirements of the other class are very complex or a series of members must come from a single source, using interfaces is often the best solution. Having to hook up several delegates or several events becomes tedious and error prone when a single reference to an object that implements a particular interface is so simple. Imagine if implementing a Windows Form wasn’t as simple as deriving from Form and having to register a number of delegates or events.

If you find that implementers of the interface would find default or base implementation for them to be useful, implementing that interface may best be done with an abstract class.

Our Invoice class is a good example of something that can be more loosely coupled through interface-based design. It currently implements the calculation of grand totals through interface-based design and the strategy pattern. This could have easily been implemented through direct use of a particular class. For example:

/// <summary>
/// Service to enapsulate calculation of
/// grand totals.
/// </summary>
public class InvoiceGrandTotalService
{
public float CalculateGrandTotal(float invoiceSubTotal,
float invoiceTotalTax)
{
return invoiceSubTotal + invoiceTotalTax;
}
}

/// <summary>
/// Invoice class that uses
/// <seealso cref="InvoiceGrandTotalService"/>
/// </summary>
public class Invoice
{
InvoiceGrandTotalService invoiceGrandTotalService =
new InvoiceGrandTotalService();
public List<InvoiceLineItem> InvoiceLineItems { get; set; }
public Invoice(IEnumerable<InvoiceLineItem>
invoiceLineItems)
{
InvoiceLineItems = new
List<InvoiceLineItem>(invoiceLineItems);
}
public float CalculateGrandTotal(float invoiceSubTotal,
float invoiceTotalTax)
{
return invoiceGrandTotalService.CalculateGrandTotal(
invoiceSubTotal, invoiceTotalTax);
}
//...
}

In this example, we’ve created the InvoiceGrandTotalService class that contains the CalculateGrandTotal method. We then instantiate this class in the Invoice class and make reference to it in the CalculateGrandTotal method.

We’ve given away the surprise with this refactoring. We’re obviously going to replace direct use of the class with an interface. Since we essentially need a reference to an object right from the start, and to effectively loosen the coupling, we begin refactoring by accepting a reference to an IInvoiceGrandTotalStrategy object in the constructor. We then change our InvoiceGrandTotalService field to an IInvoiceGrandTotalStrategy field and initialize it in the constructor. We finish our refactoring by replacing references from invoiceGrandTotalServcie to invoiceGrandTotalStrategy. The resulting refactoring will look similar to the following:

/// <summary>
/// Invoice class that uses
/// <seealso cref="IInvoiceGrandTotalStrategy"/>
/// </summary>
public class Invoice
{
private IInvoiceGrandTotalStrategy
invoiceGrandTotalStrategy;
public List<InvoiceLineItem> InvoiceLineItems { get; set; }
public Invoice(IEnumerable<InvoiceLineItem>
invoiceLineItems,
IInvoiceGrandTotalStrategy invoiceGrandTotalStrategy)
{
InvoiceLineItems = new
List<InvoiceLineItem>(invoiceLineItems);
this.invoiceGrandTotalStrategy =
invoiceGrandTotalStrategy;
}
public float CalculateGrandTotal(float invoiceSubTotal,
float invoiceTotalTax)
{
return invoiceGrandTotalStrategy.CalculateGrandTotal(
invoiceSubTotal, invoiceTotalTax);
}
//...
}

If you find that the relationship between the two classes is the invocation of one or two methods that return or update data, you may find that delegates are the best way of refactoring.

LEAVE A REPLY

Please enter your comment!
Please enter your name here