Although Dependency Injection is frequently used to support numerous use cases, probably its most obvious application is to make testing simpler.
A fantastic application development principle called dependency injection will make your code incredibly flexible, reusable, and uncoupled. With the ever-changing complexity of application development, the concept behind Dependency Injection is to keep your code decoupled so you may add, remove, and update your code fast and effectively. Because we should only be working on the dependencies we need when programming is uncoupled, a corollary is that we are less likely to break the code.
Dependency Injection is a design pattern that eliminates program dependencies. In this situation, we offer data from an external source, such as an XML file. Our code becomes more decoupled as a result, which facilitates testing.
Writing clean code is quite important in software engineering. It makes the experience of collaboration, maintenance, and development quite good. Clean code is a reader-focused development style that produces software that’s easy to write, read and maintain. With that principle in mind, a more modular and decoupled codebase makes our lives easier, even for testing, without any sophisticated container or framework.
In this blog, we will demonstrate how to write clean code using dependency injection in Java.
In this blog, we will cover:
- What is Clean Code & its Characteristics?
- What is Dependency Injection & its Roles, Benefits & Types?
- Dependency Injection in Java
- Frameworks & Libraries used by Dependency Injection
- Spring & Dependency Injection in Spring
- Hands-on
- Conclusion
What is a Clean Code?
“Any fool can write code that a computer can understand. Good programmers write code that humans can understand.” – Martin Fowler
Some issues in programming are universal, leading to general rules. However, each programming language and paradigm has its own set of peculiarities, so we must embrace methods that are appropriate.
A general definition of clean code is code that is simple for any developer to read and modify.
Characteristics of Clean Code
Clean coding practices result in codebases that have a number of distinguishing traits. Let’s go over a few of these qualities:
- Focused: A specific problem should be addressed by a piece of code. Nothing that is blatantly unrelated to solving the stated problem should be done. This holds true for all abstraction levels in the codebase, including method, class, package, and module.
- Simple: This is by far the most crucial and frequently disregarded aspect of clean code. To accomplish the intended results, the software design and execution must be as straightforward as possible. A codebase becomes more error-prone, challenging to read, and difficult to maintain as it becomes more complex.
- Testable: While being straightforward, clean code must address the issue at hand. The codebase must be simple to understand and test, preferably automatically. This makes it simpler to alter the codebase without breaking anything and helps determine the baseline behavior of the codebase.
What is Dependency Injection?
A design pattern in which an object or a function receives other objects or functions, on which it depends, is called dependency injection. It is a form of inversion control, where it separates the concerns of creating objects and using them to construct loosely coupled programs. Dependency Injection ensures that the object or function which needs to use a given service, need not know how to construct that particular service. Instead, the receiving ‘client’ (object or function) is given its dependencies by an external code (an ‘injector’), which it doesn’t require to know of.
Dependency Injection makes it easier for you to adhere to the single responsibility and dependency inversion concepts of SOLID.
The client only needs to declare the interfaces of the services it uses & not the actual implementations of them. This way you can easily change the services used in runtime, without recompiling the source code.
The main principle of Dependency Injection is to reduce the coupling and move the binding of objects out of the dependent classes. Dependency Injection solves problems such as:
- How is it possible for a class or application to be independent of the way its objects are made?
- How is it possible to specify the creation of objects in different configuration files?
- How can the application support different configurations?
The 4 roles in Dependency Injection
There are 4 roles involved in Dependency Injection:
- Service: A service is any class that carries useful functionality.
- Client: A client is any class that utilizes the service.
- Interface: An interface created by the service and used by the client. The client only needs to know the names and APIs of dependencies.
- Injector: The injector is also known as assembler, container, provider, or factory. It introduces or injects the services into the client.
Benefits of Dependency Injection
- The object creation process gets decoupled; hence boilerplate code is reduced.
- Quickly make changes in the dependencies class without affecting the main class, which is using the dependencies.
- Using Mock Dependency for Testing, thus making the testing process simple.
- Helps to enable Loose Coupling.
- Extending the application becomes easier
Assume that we have a mobile Manufacturing factory.
Mobile phones are built in an assembly unit. But a Mobile phone requires a battery, camera, speaker, keypad, display screen, etc. An assembly unit is dependent on all the things, and hence these all constitute the dependencies.
Let’s make Mobile Manufacturing a bit simpler.
If we had some provider companies that provide you with the battery, camera, keypad, speaker, and display for cheaper, we wouldn’t need to make them in our factory, and it would also reduce the maintenance task. And now we can focus only on our main task.
So we see that by injecting dependencies, Our task has become much more manageable!
Types of Dependency Injection
We can achieve dependency injection in various ways. Let’s discuss them in detail.
- Constructor Injection: Constructor injection is the process of using the constructor to pass in the dependencies of a class. You should use constructor injection when your class has a dependency that the class requires in order to work properly.
- Method Injection: In method injection, we pass the object of dependencies (concrete class) into the method of the dependent class.
- Property/Setter Injection: In this type, we pass the object of the dependencies (concrete class) by using a setter method present in the dependent class.
Dependency Injection in Java
You can use any programming language to build Dependency Injection – like Java. Dependency Injection in Java helps us have a more modular and decoupled codebase.
Whenever a Java class uses an instance of some other class, it is said to be dependent on that class. This is known as class dependency. For instance, this service class is a dependency of a class that uses a logger service.
Java classes should ideally be as separate from one another as possible. This improves the likelihood of these classes being reusable and enables independent testing of the classes.
Why use a framework for Dependency Injection?
A dependency Injection framework simplifies and manages the instantiation, life-cycle, and destruction of the objects. It simplifies the initialization of dependencies. There are many dependency Injection frameworks like Spring, Google Guice, etc.
Frameworks and Libraries used by Dependency Injection
What is Spring?
Spring functions as a factory of beans even though it is simply a container for beans. The injection in Spring is either done via setter injection or via constructor injection.
Dependency Injection in Spring
There are two techniques to insert dependencies into the Spring framework:
- By Constructor
- By Setter method
For DI, Spring offers a compact container, such as the Spring core container. In Spring, the injection is either carried out through setter injection or constructor injection. These classes that Spring manages must adhere to the JavaBean specification. Classes are also referred to as beans or as Spring beans in the context of Spring.
Why are we using Spring Framework?
Till now we know that by using Dependency Injection, we make sure that dependencies are not hardcoded in the class, it’s injected by the entities outside the class. Entities in the above case are some different classes. And these classes we need to write on our own. Spring Injects the right dependencies into the object, we don’t have to write these different classes. Hence it helps us!
Challenges of Dependency Injection without Spring Framework
- Bit Complex to learn
- Compile-time errors are pushed to run time
- DI can sometimes cause management issues and other problems
Hands-on
Let’s create a ‘LiIONBattery’ class and ‘Battery’ interface for our mobile manufacturing.
interface Battery {
String importOrigin();
double cost();
}
public class LiIONBattery implements Battery {
private double cost;
public double getCost() {
return cost;
}
public void setCost(double cost) {
cost = this.cost;
}
@Override
public String importOrigin() {
return "Japan";
}
@Override
public double cost() {
return cost;
}
}
Example without using Dependency Injection
public class Mobile {
private Battery battery = new LiIONBattery();
//other codes
}
The above Mobile class creates its own Battery object.
Example with Dependency Injection
public class Mobile{
private Battery battery;
/*
* With dependency injection, you don't need to instantiate
* the dependencies
* They are instantiated in another code and provided to
* this class, when needed
*/
//Constructor Dependency Injection
Public Mobile (Battery battery) {
System.out.println("Instantiated via constructor");
this.battery = battery;
}
//Setter based DI
void setBattery(Battery battery) {
System.out.println("instantiated via setter")
this.battery =battery;
}
//other codes
}
In the above example, we have seen the implementation of dependency injection in Java. It tells how it makes our code less coupled and helps us write clean code. Further, we will learn dependency injection using the Spring framework, which is famous for Java web development.
Example with Dependency Injection using Spring Framework
Here is pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>Spring</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-site-plugin</artifactId>
<configuration>
<locales>en,fr</locales>
</configuration>
</plugin>
</plugins>
</build>
<dependencies> <!--https://mvnrepository.com/artifact/org.springframework/spring-core -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.3.19</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.17.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-beans -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.3.19</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.19</version>
</dependency>
</dependencies>
</project>
public class Mobile{
private Battery battery;
//Constructor Dependency Injection
Public Mobile (Battery battery) {
System.out.println("Instantiated via constructor");
this.battery = battery;
}
//Setter based DI
void setBattery(Battery battery) {
System.out.println("Instantiated via Setter")
this.battery =battery;
}
Public void getBattery() {
return this.battery;
}
public static void main(String[] args) {
/*
There are different implementations of BeanFactory available. We have used XMlBeanFactory Hence we will provide XMl file
*/
BeanFactory beanFactory = new XmlBeanFactory(new FileSystemResource("Spring.xml"));
Mobile mobile1 = (Mobile)beanFactory.getBean("ConstructorInjection");
Mobile mobile2 = (Mobile)beanFactory.getBean("SetterInjection");
//other codes
}
Here is Spring.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="LiIONBattery" class="LiIONBattery">
<property name="cost" value="30000.00"/>
</bean>
<bean id="ConstructorInjection" class="Mobile">
<constructor-arg index="0" ref="LiIONBattery"/>-
</bean>
<bean id="ConstructorInjection" class="Mobile">
<property name="battery" ref="LiIONBattery"/>-
</bean>
</beans>
Conclusion
In this blog, we saw what Dependency Injection is, its role, benefits & types. We also covered different frameworks & libraries in dependency injection, DI in Java & Spring. At last, we demonstrated how to write clean code with dependency injection in Java. It is important to note that, for chained and nested dependencies, implementing dependency injection will become quite complicated. IOC containers help us map the dependencies effectively if we have a chain of nested dependencies. Constructor Injection is the most used type of injection when it comes to implementing Dependency Injection. If we need to pass different dependencies on every method call then we use method injection. Property injection is used less frequently. We will come up with more such use cases in our upcoming blogs.
Meanwhile …
If you are an aspiring Java developer and want to explore more about Spring Framework, here are a few of our blogs for your reference:
- https://www.workfall.com/learning/blog/how-to-build-spring-boot-freemarker-form-validation/
- https://www.workfall.com/learning/blog/how-to-create-ci-cd-workflow-using-aws-codestar/
Stay tuned to get all the updates about our upcoming blogs on the cloud and the latest technologies.
Keep Exploring -> Keep Learning -> Keep Mastering
At Workfall, we strive to provide the best tech and pay opportunities to kickass coders around the world. If you’re looking to work with global clients, build cutting-edge products and make big bucks doing so, give it a shot at workfall.com/partner today!