Loose coupling in practice: why interfaces matter in C#
Posted: (EET/GMT+2)
We often talk about "loose coupling" in C#, but it only becomes real when you see how much easier it makes testing and refactoring. One of the simplest ways to loosen coupling is to introduce small, meaningful interfaces between your components.
Let's take a tiny example: a logging class. Originally, you might have code like this:
public class Calculator
{
private readonly Logger _logger = new Logger();
public int Add(int a, int b)
{
_logger.Write("Adding numbers");
return a + b;
}
}
This works, but the Calculator is now tightly bound with a specific
Logger implementation. Testing becomes harder this way.
With an interface, the shape becomes cleaner:
public interface ILogger
{
void Write(string message);
}
public class ConsoleLogger : ILogger
{
public void Write(string message) => Console.WriteLine(message);
}
public class Calculator
{
private readonly ILogger _logger;
public Calculator(ILogger logger)
{
_logger = logger;
}
public int Add(int a, int b)
{
_logger.Write("Adding numbers");
return a + b;
}
}
Now your Calculator depends on some behaviour, not a concrete class.
This makes it testable, because during testing time you can easily switch the actual implementation to something completely different (mockable). This arrangement is also easier to extend in the future, even if it requires a little more typing.
Loose coupling isn't complicated. It usually starts with a small interface and a constructor parameter. Simple changes can make your codebase far more flexible.