...
Both The Elements of Java Style [Vermeulen 2000] and the JPL Java Coding Standard [Havelund 2010] require that the dependency structure of a package
...
must never contain cycles; that is, it must be representable as a directed acyclic graph (DAG).
Eliminating cycles between packages has several advantages:
- Testing and maintainability. Cyclic dependencies magnify the repercussions of changes or patches to source code. Reducing the repercussions of changes eases simplifies testing and improves maintainability. Inability to perform adequate testing because of cyclic dependencies is a frequent source of security vulnerabilities.
- Reusability. Cyclic dependencies between packages require that the packages be released and upgraded in lockstep. This requirement reduces reusability.
- Releases and builds. Avoiding cycles also helps to steer the development toward an environment that fosters modularization.
- Deployment. Avoiding cyclic dependencies between packages reduces coupling between packages. Reduced coupling reduces the frequency of runtime errors such as
ClassNotFoundError. This, in turn, simplifies deployment.
Noncompliant Code Example
This noncompliant code example contains packages named Account account and User user that consist of the classes AccountHolder, User, and UserDetails respectively. The class UserDetails extends from AccountHolder because a user is a kind of account holder. The class AccountHolder depends on a few nonstatic utility methods method defined in UserDetails and must declare and use its instancethe User class. Likewise, the UserDetails depends on AccountHolder but instead chooses to extend from by extending it.
| Code Block | ||
|---|---|---|
| ||
package Accountaccount; import user.User.*; public class AccountHolder { private UserDetailsUser uduser; //public Usesvoid a class defined in package User setUser(User newUser) {user = newUser;} synchronized void depositFunds(String username, double amount) { // Use a utility method of UserDetailsUser to check whether username exists if (uduser.exists(username)) { // Deposit the amount } } protected double getBalance(String accountNumber) { // returnReturn the account balance return 1.0; } } | ||
| Code Block | ||
| ||
package Useruser; import Accountaccount.*AccountHolder; public class UserDetails extends AccountHolder { public synchronized double getUserBalance(String accountNumber) { // Use a method of AccountHolder to get the account balance return getBalance(accountNumber); } } public class User { public boolean exists(String username) { // Check whether user exists return true; // Exists } } |
Compliant Solution
The tight coupling between the classes in the two packages can be weakened by introducing an interface called BankApplication in a third package, Bank bank. The cyclic package dependency is eliminated by ensuring that the AccountHolder does not use an instance of UserDetails but depend on User but instead relies on the interface by importing the Bank bank package (and not by implementing the interface).
...
| Code Block | ||
|---|---|---|
| ||
package Bankbank; public interface BankApplication { void depositFunds(BankApplication ba, String username, double amount); double getBalance(String accountNumber); double getUserBalance(String accountNumber); boolean exists(String username); } | ||
| Code Block | ||
| ||
package Accountaccount; import Bankbank.*BankApplication; // Import from a third package class AccountHolder { private BankApplication ba; public void setBankApplication(BankApplication newBA) { ba = newBA; } public synchronized void depositFunds(BankApplication ba, String username, double amount) { // Use a utility method of UserDetails // to check whether username exists if (ba.exists(username)) { // Deposit the amount } } public double getBalance(String accountNumber) { // Return the account balance return 1.0; } } | ||
| Code Block | ||
| ||
package Useruser; import Accountaccount.*AccountHolder; // One -way dependency import Bankbank.*BankApplication; // Import from a third package public class UserDetails extends AccountHolder implements BankApplication { public synchronized double getUserBalance( String accountNumber) { // Use a method of AccountHolder to get the account balance return getBalance(accountNumber); } public boolean exists(String username) { // Check whether user exists return true; } } | ||
| Code Block | ||
| ||
package Implementer; import Bank.*; import Account.*; import User.*; class BankOperations { private BankApplication ba; public BankOperations(BankApplication ba) { this.ba = ba; } public void doUserActions() { System.out.println(ba.exists("user")); System.out.println(ba.getUserBalance("1111")); } public static void main(String[] args) { AccountHolder ac = new AccountHolder(); ac.depositFunds(new UserDetails(), "user", 1.0); // Pass an interface argument BankOperations bo = new BankOperations(new UserDetails()); bo.doUserActions(); } } |
The interface BankApplication appears to contain superfluous methods such as depositFunds() and getBalance(). These methods are present so that if the subclass overrides them, the superclass retains the capability of internally invoking the subclass's methods polymorphically (for example, calling ba.getBalance() with an overridden implementation of the method in UserDetails). One consequence of using this implementation solution is that methods declared in the interface are required to be public in the classes that define them.
...
Applicability
Cyclic dependencies between packages can result in fragile builds. A security vulnerability in a package can easily percolate to other packages.
...
Automated Detection
| SeverityTool | LikelihoodVersion | Remediation CostChecker | PriorityDescription | Level | |
|---|---|---|---|---|---|
DCL63-JG | low | probable | medium | P4 | L3 |
Bibliography
| Parasoft Jtest |
| CERT.DCL60.ACD | Ensure that files do not contain cyclical dependencies |
Bibliography
| [Havelund 2010] | JPL Coding Standard, Version 1.1 |
§1.2.5, |
Chapter 1: "OO Principles and Patterns"
"Acyclic Dependencies Principle" |
...
Chapter 1, "OO Principles and Patterns" | |
| [Vermeulen 2000] | The Elements of Java Style |
...