Coupling in C#
In the programming world, after the advent of object-oriented programming, classes and objects came into existence and became the most preferred type of programming. Though the programming language would differ (Java, C#, etc..,) the underlying concept always remained as object-oriented programming.
Here, classes, objects, methods play a crucial role in setting up the structure of the code. Initially, the three-tier architecture pattern was followed and then later moved on to MVC. However, in both these patterns, classes were created based on roles and responsibilities. Say, for example, a class could be created to do DB operations, similarly one for the business layer, one for external calls or any specific functions, and so on.
There will be a necessity for the business layer to connect to the database layer, and similarly the presentation layer to interact with the business layer. In these cases, the classes have to communicate with each other. Ultimately, the object of the classes will be used to call the exposed methods.
How the classes are connected is referred to as Coupling. There are two ways of coupling in C#. Let's see in detail about them in this blog along with a brief understanding of what is coupling.
Table of contents
- What is Coupling?
- Tightly Coupled
- Loosely Coupled?
- Conclusion
What is Coupling?
Here is the classic definition of coupling - It is the degree to which the software components depend on each other.
Coupling in C# describes the relationship between modules in C#, how the classes and objects are connected, and also how dependent they are on each other. This also describes the flexibility and reusability part of the code.
A coupling as such is needed for successful communication between modules. But, how the modules are coupled determine the standard of the code. Let's see in detail the two types of coupling now.
Tightly Coupled
Think of it this way - your fingers are tightly coupled with the hand, which makes it hard to change the position of the fingers and their location. This means that changing the position of fingers is close to impossible and requires redesigning of the hand. This is an example of being tightly coupled.
The classes and objects are very much interdependent, so it becomes difficult to reuse the code. When there are many dependent components, then it becomes difficult to maintain the code for enhancements.
Let us take an example, to understand things better.
namespace PARTECHCoupling
{
class Program
{
static void Main(string[] args)
{
var fileReader = new FileReader(@"D:\PARTECH\test.txt");
fileReader.StartReading();
fileReader.SaveData();
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
}
public class FileReader
{
private readonly string _filename;
private string _contents;
public FileReader(string filename)
{
_filename = filename;
}
public void StartReading()
{
_contents = File.ReadAllText(_filename);
}
public void SaveData()
{
var saver = new FileSaver(_filename.Replace(".txt", ".doc"), _contents);
saver.SaveFile();
}
}
public class FileSaver
{
private readonly string _filename;
private readonly string _contents;
public FileSaver(string filename, string contents)
{
_filename = filename;
_contents = contents;
}
public void SaveFile()
{
File.WriteAllText(_filename, _contents);
}
}
}
In the above code, we have a Program class from where the code begins to execute. Here the program class has a main method and it holds the code to create an object for filereader which expects a file name. And inside the filereader class, there are two methods, one to read the data from the file and another one to save the data to a file. The save method creates an object for another class which is then used to save the data to a different file.
Since the two classes are concrete or tightly coupled, and as they know the entire implementation of the class, it is difficult to make additional changes. This is simply because the changes have to be done at a lot of places. Imagine a similar kind of coupling in a much larger application. It will be extremely tricky to maintain and support the code.
Loosely Coupled
Loosely coupled can be explained with the help of head as an example. Take any type of hat, you will be able to fit it on your head. For example, a cowboy hat can fit on top of it, so can a chef's hat. Here, the head and the cap are loosely coupled as both are not interdependent. This makes it much less complex and ensures code reusability.
From a code perspective, the class and objects are not interdependent so it becomes easy to replace, reuse, maintain the code. Let's see how to achieve the same functionality(discussed in the previous section) using loosely coupled code.
namespace PARTECHCoupling
{
class Program
{
static void Main(string[] args)
{
var fileReader = new FileReader(@"D:\PARTECH\test.txt");
fileReader.StartReading();
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
}
public class FileReader
{
private readonly string _filename;
private string _contents;
public FileReader(string filename)
{
_filename = filename;
}
public void StartReading()
{
_contents = File.ReadAllText(_filename);
}
public void SaveData()
{
IFileSaver saver = new FileSaver();
saver.SaveFile(_filename, _contents);
}
}
public interface IFileSaver
{
public void SaveFile(string filename, string contents);
}
public class FileSaver : IFileSaver
{
public void SaveFile(string filename, string contents)
{
File.WriteAllText(filename, contents);
}
}
}
Here in the above example, the main method and the FileReader class look the same. But, the change exists in the creation, implementation, and consumption of the FileSaver class. To make it loosely coupled, an interface has been introduced (IFileSaver). Now how does it improve the coupling?
For example, currently, we are saving the file in txt format. In future, if there is an option to change to other formats, the interface will be inherited by the implementing class. The same can be consumed inside the FileReader class with only minimal code changes within it as the methods defined in the interface will be implemented by the inheriting class.
Conclusion
It is necessary to build modular code which enables swapping of code as required. And it is possible in achieving it through a loosely coupled way of building the code. And in smaller straightforward applications it is still okay to go with tightly coupled code, but it would be a major revamp when huge changes are coming your way.