You are viewing an old version of this page. View the current version.

Compare with Current View Page History

Version 1 Next »

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:

  • Maintainability: It is preferable to make a change somewhere and limit the repercussions to as few packages as possible (ideally just one) as opposed to having to monitor or refine numerous packages.
  • Reusability: When a new version of a package is released, clients who reuse it do not have to test their existing code bases for compatibility with other packages that this particular package depends on. Sometimes, the reusable package evolves only to accommodate the changes to packages that it depends on.
  • Releases and builds: Avoiding cycles also helps to steer the development towards an environment that fosters modularization. Owners of different packages are also redeemed from relying on other bleeding-edge packages.
  • Deployment: By resolving the cycles, deployment is simplified, as runtime errors like the infamous ClassNotFoundError, are reduced to a minimum by virtue of the toned down coupling between packages.

Noncompliant Code Example

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); 
    }
    protected boolean exists(String username) { /* check whether user exists*/ }
  }

Compliant Solution

TODO


Risk Assessment

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

References

[[Martin 96]]
[[Knoernschild 01]]


MSC05-J. Make sensitive classes noncloneable      49. Miscellaneous (MSC)      MSC30-J. Generate truly random numbers

  • No labels