...
This noncompliant code example features two different packages named Account and User that consist of the classes AccountHolderClass and UserClass, respectively. The class UserClass extends from AccountHolderClass as an account holder can be any kind of user or owner. AccountHolderClass depends upon on a few non-static utility methods defined in UserClass and must declare and use its instance. Likewise, the UserClass depends on AccountHolderClass but instead chooses to extend from it. This vicious use circle is one recipe for a circular cyclic dependency.
| Code Block | ||
|---|---|---|
| ||
package Account;
import User.*;
class AccountHolderClass {
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 AccountHolderClass {
protected synchronized double getUserBalance(String accountNumber) {
// use a method of AccountHolderClass to get the account balance
return getBalance(accountNumber);
}
public boolean exists(String username) { /* check whether user exists*/ }
}
|
...
The tight coupling between the classes in the two packages can be weakened by introducing an interface called BankApplication in a third package, Bank. The circular cyclic dependency is eliminated by ensuring that the AccountHolderClass does not use an instance of UserClass, but instead relies on the interface by importing the Bank package. In this compliant solution, such functionality is achieved by introducing a parameter of the interface type BankApplication to the depositFunds() method. This gives the AccountHolderClass a solid contract to bank upon. Additionally, UserClass implements the interface and provides concrete implementations of the methods while at the same time, inheriting the others from AccountHolderClass.
It might appear that the interface BankApplication contains superfluous methods such as depositFunds() and getBalance(). They are present so that if the subclass overrides these superclass methods, the latter retains the capability of internally invoking the subclass' methods polymorphically (like such as calling ba.getBalance() here, with a custom implementation of the method in UserClass).
...
Cyclic dependencies between packages can lead to fragile builds. A security vulnerability in any a package will can easily percolate to several other packages.
...