Abstract
Dependency injection (DI) is generally known to improve maintainability by keeping application classes separate from the library. Particularly within the Java environment, many applications use the principles of DI to improve maintainability. There exists some work that provides an inference on the impact of DI on maintainability, but no conclusive evidence is provided. The fact that there are no publicly available tools for quantifying DI makes such evidence more difficult to produce. In this paper, we propose two novel metrics, dependency injection-weighted afferent couplings (DCE) and dependency injection-weighted coupling between objects (DCBO), to measure the proportion of DI in a project based on weighted couplings. We describe how DCBO can serve as a more meaningful metric in assessing maintainability when DI is also considered. The metric is implemented in the CKJM-Analyzer, an extension of the CKJM tool to perform static analysis on DI detection. We discuss the algorithmic approach behind the static analysis and prove the soundness of the tool using a set of open-source Java projects.
Keywords: dependency injection, coupling, maintainability, Java, software architecture, static analysis
1. Introduction
Software development has grown increasingly dependent on external libraries built by other companies. While this provides convenience in development, it significantly increases the cost of software maintenance, especially for changes involving dependencies with libraries. The use of external libraries also requires significant overheads, such as extra code to be imported and compiled, resulting in performance bottlenecks. Additionally, external libraries can introduce security vulnerabilities unknown to the developers using those libraries. These issues are exacerbated if the external libraries are open-source and maintained by the community, resulting in inconsistent updates and lack of maintenance from the original developers.
Component frameworks (e.g., Spring) help mitigate development costs. A key feature of component frameworks for object-oriented programming (OOP) is dependency injection (DI). DI is a pattern of sending (“injecting”) necessary fields (“dependencies”) into an object, instead of requiring the object to initialize those fields itself. Existing literature suggests that the use of DI can help improve the maintainability of software systems. On the other hand, there are also warnings against using DI due to possible negative effects.
A software quality metric often used to measure maintainability is coupling between objects (CBO). CBO is the total number of couplings present within the software system, or the sum of the system’s afferent couplings (CA) and efferent couplings (CE). CA counts how many other classes use the class being analyzed, while CE counts how many classes the class being analyzed uses. Therefore, when a coupling exists between two objects, the object being depended on will increase its CA value by 1, and the object depending on the other object will increase its CE value by 1, generating an overall CBO value of 2. Generally, higher CBO yields lower maintainability because of the increased complexity of the system.
In this paper, we present two novel metrics, dependency injection-weighted afferent couplings (DCE) and dependency injection-weighted coupling between objects (DCBO), to analyze DI and assess the impact of DI on software maintainability and its tool support, CKJM-Analyzer, which is an extension of the CKJM tool . DCE weighs each efferent coupling depending on whether it is soft-coupled (e.g., with DI) or hard-coupled (e.g., with the new keyword, or with using an object generator that requires parameter information from the user). DCBO utilizes DCE in place of CE as a weighted metric of overall coupling. CKJM-Analyzer is a cross-platform command line interface (CLI) with two primary goals:
(i) develop a standard operating procedure to iteratively analyze Java projects for CKJM metrics
(ii) count the instances of the DI pattern in Java projects to determine the DI proportion. We validate the metric and tool with a set of open-source Java projects.
The remainder of the paper is organized as follows. Section 2 presents a background on DI. Section 3 describes DCBO and its algorithmic approach implemented in the CKJM-Analyzer tool. Section 4 describes the evaluation of CKJM-Analyzer on experimentally generated projects and open-source projects. Section 5 discusses the results with regards to the impact of DI on maintainability, the effect of DCBO on coupling analysis, the limitations and potential future work. Section 6 gives an overview of the related work on the effect of DI in software systems, as well as work in measuring coupling weight. Section 7 concludes the paper with a discussion on future research work.
2. Dependency Injection
Dependency injection (DI) is a specific form of the dependency inversion principle, which is a pattern that suggests higher-level objects should dictate most of the complex logic in the system and also create dependencies for lower-level objects to use. DI is a subset of this principle because it highlights how lower-level objects should rely on higher-level objects for their dependencies. DI is a design pattern to improve the maintainability of software systems by reducing developer effort in adding coupling through injecting dependencies in classes using an external injector, which is a class object or file (e.g., an XML-based configuration file in Spring Framework). As coupling is reduced, consequently the complexity of classes is also diminished. DI also makes it easier to pinpoint dependency-related errors as dependency injection is localized in one place (viz. the injector).
2.1 Types of dependency injection
A dependency is typically injected in four ways:
1.Constructor Injection: Dependencies are provided through a class constructor. This is known as Constructor No Default (CND).
2.Setter Injection: Dependencies are provided through setter methods. This is known as Method No Default (MND).
3.Interface Injection: Dependencies are provided through an interface that the client must implement.
4.Field Injection: Dependencies are injected directly into class fields. This is often considered less favorable due to the increased difficulty in testing.
2.2 Benefits of dependency injection
1.Improved Testability: By decoupling dependencies, classes can be tested independently by mocking or stubbing dependencies.
2.Simplified Code Maintenance: Changes in dependency implementation do not affect the dependent class.
3.Enhanced Flexibility and Reusability: Dependencies can be swapped out without altering the dependent class.
4.Decreased Coupling: Reduces the direct dependency of a class on its collaborators, adhering to the principle of least knowledge.
2.3 Challenges of Dependency Injection
1.Learning Curve: Developers need to understand how DI frameworks work and how to configure them properly.
2.Complex Configuration: Managing configurations for complex applications can become cumbersome.
3.Potential Overhead: Improper use of DI can lead to performance overhead and increased complexity.
3. Dependency Injection-Weighted Metrics
To quantitatively measure the impact of DI on maintainability, we propose two novel metrics: dependency injection-weighted afferent couplings (DCE) and dependency injection-weighted coupling between objects (DCBO).
3.1 Dependency Injection-Weighted Afferent Couplings (DCE)
DCE is designed to weigh each efferent coupling depending on whether it is soft-coupled (e.g., using DI) or hard-coupled (e.g., using the new keyword). This allows for a more nuanced view of how dependencies are managed within a project.
3.2 Dependency Injection-Weighted Coupling Between Objects (DCBO)
DCBO utilizes DCE in place of traditional CE as a weighted metric of overall coupling. This metric provides a better assessment of maintainability by accounting for the nature of the couplings within the system.
3.3 Implementation in CKJM-Analyzer
CKJM-Analyzer is an extension of the CKJM tool that incorporates DCE and DCBO metrics. It performs static analysis to detect instances of DI and calculates the proportion of DI within a project.
3.3.1 Algorithmic Approach: The algorithm used by CKJM-Analyzer involves:
1.Static Analysis: Parsing the source code to identify dependency injection patterns.
2.Coupling Calculation: Weighing each coupling based on its type (soft or hard) and calculating DCE and DCBO values.
3.Metric Reporting: Providing detailed reports on the DI proportion and its impact on maintainability.
3.3.2 Tool validation: To validate CKJM-Analyzer, we tested it on a set of open-source Java projects. The results showed a significant correlation between the use of DI and improved maintainability, as indicated by lower DCBO values.
4. Evaluation of CKJM-Analyzer
4.1 Experiment Setup
We selected a diverse set of open-source Java projects for evaluation. Each project was analyzed using CKJM-Analyzer to measure the DCE and DCBO metrics.
4.2 Results
The analysis revealed that projects with higher proportions of DI exhibited lower DCBO values, indicating better maintainability. Projects with minimal DI had higher DCBO values, suggesting increased complexity and lower maintainability.
4.3 Discussion
The results support the hypothesis that DI contributes to improved maintainability. By reducing hard couplings and promoting flexible dependency management, DI helps maintain a clean and modular codebase.
5. Impact of DI on Maintainability
5.1 Benefits
1.Reduced Complexity: DI helps maintain a clear separation of concerns, reducing the overall complexity of the system.
2.Easier Refactoring: Changes in dependencies can be managed more easily, facilitating refactoring and enhancing code evolution.
3.Improved Collaboration: DI promotes modular design, making it easier for multiple developers to work on different parts of the system simultaneously.
5.2 Limitations
1.Performance Overhead: Improper use of DI can lead to runtime performance issues due to the overhead of dependency resolution.
2.Complex Configuration: Managing complex dependency graphs can be challenging, especially in large applications.
5.3 Future Work
Future research can explore the development of more sophisticated tools for DI detection and analysis, as well as investigate the impact of DI in other programming environments beyond Java.
6. Related Work
6.1 DI in Software Systems
Several studies have explored the benefits of DI in software systems. These studies highlight how DI can improve testability, flexibility, and overall maintainability.
6.2 Measuring coupling weight
Previous research has proposed various metrics for measuring coupling in software systems. However, these metrics often do not account for the nature of the coupling (soft vs. hard). Our proposed DCE and DCBO metrics address this gap.
7. Conclusion
Dependency injection (DI) plays a crucial role in improving the maintainability of software systems. By promoting loose coupling and flexible dependency management, DI helps maintain a clean and modular codebase. Our proposed metrics, DCE and DCBO, provide a quantitative measure of DI's impact on maintainability. The CKJM-Analyzer tool demonstrates the practicality of these metrics through static analysis of open-source Java projects. Future work can further refine these metrics and explore their applicability in other programming environments.
8. References