Skip to content

Analyzing a Tightly Coupled Application

Last updated on April 1, 2019

Dependency injection is a vast topic in itself. However, it is difficult to find everything we should know about it in one place. And so, I thought of creating my own series of learning about topic. In this part of the series we will look into a tightly coupled application and the problems related to it.

 

Introduction

An application is said to be tightly coupled, when its classes are dependent on other concrete classes. Also, a class has the responsibility to instantiate its dependencies on its own. As a result, the application will have a build failure until all its dependencies are resolved.

 

Case Study

Consider a Commerce class which uses the ProcessPayment method of the PaymentProcessor class. If we know nothing of dependency injection, our Commerce class would look like this in code:

public class Commerce
{
    private PaymentProcessor _processor;
    public Commerce()
    {
        _processor = new PaymentProcessor();
    }

    public void ProcessPayment()
    {
        // do some work
        _processor.ProcessPayment();
    }
}

public class PaymentProcessor
{
    public void ProcessPayment()
    {
        // process payment from customer
    }
}

Because Commerce class is using the PaymentProcessor class, it has an extra responsibility of instantiating it. And in order to do so, it has to know about PaymentProcessor at compile time only.

Assume, that the user has multiple concrete implementations of PaymentProcessor. In a tightly coupled application, he/she won’t have the luxury of deciding, which concrete implementation of PaymentProcessor shall be used by the Commerce class, based on some configuration.

While it looks fine with a set of two classes, we might end up over complicating our simple application as the dependencies grow. To understand that, let’s have look at a scenario in which the PaymentProcessor class uses a service and therefore increasing the dependency level.

 

Multilevel Dependencies

Assume, that PaymentProcessor uses a CurrencyConverter service, while processing the payment. Though Commerce class remains unchanged, our PaymentProcessor class now has an additional responsibility of instantiating the service before its use. Thus, the service class must be known to the PaymentProcessor at compile time, else we will get a build failure. Refer the code below:

public class PaymentProcessor
{
    private CurrencyConverter _cc;
    public PaymentProcessor()
    {
        _cc = new CurrencyConverter();
    }

    public void ProcessPayment()
    {
        double convertedCurrency = _cc.ConvertToLocalCurrency();
        // do some work
    }
}

public class CurrencyConverter
{
    public decimal ConvertToLocalCurrency()
    {
        // return converted value
    }
}

 

Unit Testing

Unit testing the classes in our scenario is quite easy. It won’t be a problem if we add some more dependencies to the classes. However, if we have classes that talk to the database or some other sort of data store, we will be in big trouble. It would be very difficult to unit test such classes. This is because, every time we run a test, we will hit our database which might hamper our data.

In order to solve such problems, it is advisable to stop “newing-up” objects in classes. In addition, someone else has to take the responsibility of instantiating the dependencies and provide it to the dependent classes. Consequently, while unit testing, we can use mocks rather than using the actual objects.

 

Summary

  • With classes having to instantiate their dependencies (other classes), our simple application will get complicated as the level of dependencies increases.
  • The application will not compile successfully until all the dependencies are resolved at compile time only.
  • Coupled architecture limits the functionality to single implementation. It’s not possible to choose between the implementations at run time based on any configuration.
  • If classes perform DB work, it is difficult to test them without hitting the database.
  • Avoid “newing-up” objects in classes.

 

Published inDesign Patterns & Principles