Exploring the Concept of Interception in Dependency Injection (DI) for Loosely Coupled Code

Written by hackercltvodz2u0000356n4zptwviq | Published 2024/03/25
Tech Story Tags: software-engineering | dependency-injection | interception | loose-coupling | code-maintainability | single-responsibility | decorator-design-pattern | liskov-substitution-principle

TLDR Dependency Injection (DI) facilitates loosely coupled code by injecting required objects or functions into classes or functions. Interception, a component of DI, allows for the execution of code before or after a service call, promoting the Single Responsibility Principle. Interception is closely related to the Decorator pattern and supports addressing Cross-Cutting Concerns like error handling, security, and dynamic code generation in software development, resulting in cleaner and well-designed code adhering to SOLID principles.via the TL;DR App

Interception is a constituent of Dependency Injection(DI). So, to understand interception we need to first understand what Dependency Injection(DI) is.

Dependency Injection

Dependency Injection is a software engineering technique that aims to develop loosely coupled code. In this technique, a required object or function is injected into another class or function. In this way, the concern of creating them is eliminated, leading to loosely coupled code.

Software engineering techniques focus on finding more effective ways to write code. To do that, the code must be maintainable. Loose Coupling(LC) is one way to bring about maintainability. DI is also a technique that enables LC.

In the real world, software development is cumbersome because of requirement changes and unclear requirements. These changes and uncertainty make the implementation complicated. DI addresses such issues by making use of Loose Coupling.

Loose Coupling is valuable for many reasons

  • It allows late binding. Even if it is not planned initially, you can swap one service with another. It makes sense when it is used in normal applications where it is flexible to make environmental changes.

  • LC also makes it possible to add new features without modifying existing features.

  • It helps separate development into modules. This makes parallel development easier because the modules don't depend on each other. It is meaningful in large-scale projects because of time constraints.

  • As responsibilities are separated, defining the scope of codes becomes clear. This makes the maintenance of code easier. Also, finding the cause of a bug becomes easier because the scope of an issue gets shortened. Instead of modifying existing code, new classes and methods are added. This makes applying changes easier.

  • LC also makes it easier to test an application. Instead of testing direct implementation, dependencies which are interfaces are tested. Because the dependencies are injected from the outside, their instances are tested.

Interception

Interception is one of the important yields of Loose Coupling. It makes it easier to use Single Responsibility Principle. Interception states that a call from a client to a service is intercepted so that some code can be executed before or after the service call.

Interception is an implementation of the Decorator pattern. In this pattern, a decorator wraps another decorator that contains a component. The outer Decorator transfers a call to the wrapped Decorator which also delegates the call to the component contained by itself. The input or output of this call to the contained component is used by the calling decorator to do additional work.

Because Interception is designed over the Decorator pattern, it is also closely related to SOLID. In the Decorator pattern, adding additional behavior to the wrapped object is an application of the Open/Closed Principle, preparing specific scopes that make it easy to use the Single Responsibility Principle.

As a Decorator wraps another Decorator, the original implementation is replaced with another implementation of the same Abstraction. This is an application of the Liskov Substitution Principle.

These mentioned Single Responsibility, Open/Closed, and Liskov Substitution principles are some of the SOLID principles.

Here is an example of Open/Close Principle.

Log behavior is added to class above without changing existing code:

public void UpdateAccountBalance(Account account) {
    ...  -> existing codes not changed
    this.logAgent.Log(new Log("AccountBalanceUpdated", account));
}

Cross-Cutting Concerns

The fact that Dependency Injection is loosely coupled and enables the use of SOLID principles via Interception and Decorator pattern is useful when there are Cross-Cutting Concerns.

These concerns are about unrelated areas of code in an entire application. Because they cross each other in the code base, it is called Cross-Cutting Concerns.

Here are common concerns and solutions for them:

Circuits

If a resource is unavailable when an application calls it, the application must deal with this problem. An application has to fail fast and stop consuming its resources to multiple retries to recover at the time of any fault. In this case, the Circuit Breaker pattern intercepts the current flow of the application and gains the application robustness in this way.

This design pattern is designed to disconnect when a failure occurs in a software application. In this way, the failure is prevented from propagating.

When communication errors happen, continuous retries make the situation even worse. In this situation, a break may give the system time to recover. This design pattern meets this need by making the switch state closed at the time of an error in the system. When the fault is fixed, the state is switched back to open.

Exceptions

If an error occurs in an application, the code has to throw an exception. Interception can address the need for error handling by following SOLID. Dependency should not be responsible for error handling in a situation. Otherwise, it will not be consistent with Single Responsibility. Interception can deal with exception handling by performing the Open/Closed Principle.

Security

Applications have to be secure in order to disclose confidential information from unauthorized access and to get ahead of attacks on the network. In security concerns, Interception is used in a method call to check its validity. This authorization is fulfilled by performing role-based security.

This is a sample for role control to display information on a page. If the role does not match, even the page will not be rendered.

protected override void Render(HtmlTextWrtier writer) {
    User user = HttpContext.Current.Session["User"] as User;
    if (!user.Roles["AnyRoleKey"]){
	  throw new SecurityException();
    }

    base.Render(writer);
}

Dynamic Interception

While code can be generated automatically at design time, it is also possible to write code at runtime. This type of class does not have a source code file. Its code is compiled from an abstract class.

Dynamic Interception code is not automatically generated, the code implementing the aspect must be written. New classes will be dynamically emitted into the Application Domain based on the registered aspects at runtime.

Conclusion

Interception is one of the Dependency Injection components and an implementation of the Decorator pattern.

DI is loosely coupled and leads to the use of Open/Closed, Liskov Substitution, and Single Responsibility Principles with the help of the Decorator pattern. These aspects of DI are useful at times of Cross-Cutting Concerns. Also, it supports the writing of clean and well-designed code.

The Decorator pattern lets us add additional behavior to the code without changing it by wrapping current functionality.

Attributes are compiled with the code in which they are placed. Decorators are repetitive. Thus, Attributes become a better alternative to Decorators.

References


Published by HackerNoon on 2024/03/25