Several well-known adages in Object-oriented design suggest that the dependency structure of a package or module must never contain cycles, or in other words, must orchestrate a Directed Acyclic Graph (DAG).
There are several pros of eliminating cycles between packages:
ClassNotFoundError, are reduced to a minimum by virtue of the toned down coupling between packages.This noncompliant code snippet features two different packages named Account and User that consist of the classes AccountClass and UserClass, respectively. The class UserClass extends from AccountClass as one account can have many users or owners. AccountClass depends upon a few utility methods defined in UserClass and must declare its instance. Likewise, the UserClass depends on AccountClass but instead chooses to extend from it. This is one recipe for a circular dependency.
package Account;
import User.*;
class AccountClass {
private UserClass uc; // uses a class defined in package User
protected synchronized void depositFunds(String username, double amount) {
// use a utility method of UserClass to check if username exists
if(uc.exists(username)) {
// deposit the amount
}
}
protected double getBalance(String accountNumber) { /* return the account balance */ }
}
package User;
import Account.*;
class UserClass extends AccountClass {
protected synchronized double getUserBalance(String accountNumber) {
// use a method of AccountClass to get the account balance
return getBalance(accountNumber);
}
public boolean exists(String username) { /* check whether user exists*/ }
}
|
TODO. Outline below. Do not use DRAFT.
public interface BankApplication {
void depositFunds(BankApplication ba, String username, double amount);
double getBalance(String accountNumber);
double getUserBalance(String accountNumber);
boolean exists(String username);
}
class AccountClass {
public synchronized void depositFunds(BankApplication ba, String username, double amount) {
// use a utility method of UserClass to check if username exists
if(ba.exists(username)) {
// deposit the amount
}
}
public double getBalance(String accountNumber) { /* return the account balance */ return 1.0; }
}
class UserClass extends AccountClass implements BankApplication {
public synchronized double getUserBalance(String accountNumber) {
// use a method of AccountClass to get the account balance
return getBalance(accountNumber);
}
public boolean exists(String username) { /* check if user exists*/ return true; }
}
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) {
AccountClass ac = new AccountClass();
ac.depositFunds(new UserClass(), "user", 1.0);
BankOperations bo = new BankOperations(new UserClass());
bo.doUserActions();
}
}
|
Acyclic dependencies between packages can lead to fragile builds. A security vulnerability in any package will easily percolate to several other packages.
Recommendation |
Severity |
Likelihood |
Remediation Cost |
Priority |
Level |
|---|---|---|---|---|---|
MSC06-J |
low |
probable |
medium |
P4 |
L3 |
\[[Martin 96|AA. Java References#Martin 96]\] \[[Knoernschild 01|AA. Java References#Knoernschild 01]\] Chapter 1: "OO Principles and Patterns, 1.2.5 Acyclic Dependencies Principle" |
MSC05-J. Make sensitive classes noncloneable 49. Miscellaneous (MSC) MSC30-J. Generate truly random numbers