Dependency Injection - Part 3 - Implicit Vs Explicit Dependencies

If a component or class depends upon other components to complete its operations, then these other components are dependencies for this class. Our class can have these dependencies as implicit or explicit dependencies.

Introduction 
 
If a component or class depends upon other components to complete its operations, then these other components are dependencies for this class. Our class can have these dependencies as implicit or explicit dependencies. In this article, we will try to understand how it affects our application design. 

Implicit Dependencies
 
The dependencies are implicit for a class if they exist only in the code within that class, and not in its public interface. Therefore, while instantiating our class, we will not be aware of any dependency that our class may have to perform its operations. Consequently, our code may fail during execution.
 
Classes with implicit dependencies cost more to maintain than those with explicit dependencies. Also, they are more difficult to test because of their tight coupling with the collaborators. And the tight coupling among the classes and their collaborators result in more rigid and brittle designs.

Example 
  1. class Program  
  2. {  
  3.     static void Main(string[] args)  
  4.     {  
  5.         decimal paidValue = 453.23m;  
  6.         var commerce = new Commerce();  
  7.         commerce.ProcessCustomerPayment(paidValue);  
  8.     }  
  9. }  
  10.   
  11. public class Commerce  
  12. {  
  13.     public void ProcessCustomerPayment(decimal paidValue)  
  14.     {  
  15.         var currencyConverter = new CurrencyConverter();  
  16.         var paymentProcessorv = new PaymentProcessor();  
  17.         decimal currencyValue = currencyConverter.ConvertCurrency(paidValue);  
  18.         paymentProcessor.ProcessPayment(currencyValue);  
  19.         // do some work  
  20.     }  
  21. }  
  22.   
  23. public class PaymentProcessor  
  24. {  
  25.     public void ProcessPayment(decimal value)  
  26.     {  
  27.         // do some work  
  28.     }  
  29. }  
  30.   
  31. public class CurrencyConverter  
  32. {  
  33.     public decimal ConvertCurrency(decimal value)  
  34.     {  
  35.         //convert value to local currency and return  
  36.         return value;  
  37.     }  
  38. }  
As we can see, the Commerce class is tightly coupled with the PaymentProcessor and CurrencyConverter. Also, at the time of object creation for Commerce class, we can not judge what all collaborators it is going to use. Further, we will see how we can refactor this code in order to achieve loose coupling.

Explicit Dependencies
 
Being explicit about its dependencies means that a class exposes all its class-level dependencies in its constructor. However, more local ones may appear as method parameter list. It is due to the explicit declaration that we are aware of all dependencies a class has, at the time of its object construction.
 
An explicit dependency is often declared as an interface in the class constructor. Consequently, the dependency can be easily swapped out with its other implementations, whether in production or during testing or debugging. This makes them much easier to maintain and far more open to accepting any change.

Example 
  1. class Program  
  2. {  
  3.     static void Main(string[] args)  
  4.     {  
  5.         decimal paidValue = 453.23m;  
  6.         var commerce = new Commerce(new PaymentProcessor(), new CurrencyConverter());  
  7.         commerce.ProcessCustomerPayment(paidValue);  
  8.     }  
  9. }  
  10.   
  11. public class Commerce  
  12. {  
  13.     IPaymentProcessor _paymentProcessor;  
  14.     ICurrencyConverter _currencyConverter;  
  15.           
  16.     public Commerce(IPaymentProcessor paymentProcessor, ICurrencyConverter currencyConverter)  
  17.     {  
  18.         _paymentProcessor = paymentProcessor;  
  19.         _currencyConverter = currencyConverter;  
  20.     }  
  21.   
  22.     public void ProcessCustomerPayment(decimal paidValue)  
  23.     {  
  24.         // do some work  
  25.         decimal currencyValue = _currencyConverter.ConvertCurrency(paidValue);  
  26.         _paymentProcessor.ProcessPayment(currencyValue);  
  27.     }  
  28. }  
  29.   
  30. //Dependency Classes and their respective Interfaces   
  31. public interface IPaymentProcessor  
  32. {  
  33.     void ProcessPayment(decimal value);  
  34. }  
  35.   
  36. public class PaymentProcessor : IPaymentProcessor  
  37. {  
  38.     public void ProcessPayment(decimal value)  
  39.     {  
  40.         // do some work  
  41.     }  
  42. }  
  43.   
  44. public interface ICurrencyConverter  
  45. {  
  46.     decimal ConvertCurrency(decimal value);  
  47. }  
  48.   
  49. public class CurrencyConverter : ICurrencyConverter  
  50. {  
  51.     public decimal ConvertCurrency(decimal value)  
  52.     {  
  53.         //convert value to local currency   
  54.         return value;  
  55.     }  
  56. }  
In this example, the Commerce class exposes all its required collaborators in its constructor. As a result, we have a loosely coupled application. Moreover, we now have the freedom to choose among different implementations of these interfaces required by the Commerce class. Consequently, the application is now easy to maintain, test and expand.

Summary 
 
In this article, we learned about the two different ways in which a component can expose its dependencies. However, it is a good practice to explicitly expose the dependencies of a component. It makes our components easy to test, maintain and enhance.
 
We are now familiar with types of dependencies and have also studied the problems associated with a tightly coupled application. In the next part of this series, we will decouple our application without using any dependency injection container. We will also enhance our application for future purpose.