...
Several
...
well-known
...
adages
...
in
...
object-oriented
...
design
...
suggest
...
that
...
the
...
dependency
...
structure
...
of
...
a
...
package
...
or
...
module
...
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
...
- 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
...
and
...
User
...
that
...
consist
...
of
...
the
...
classes
...
AccountHolder
...
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
...
defined
...
in
...
UserDetails
...
and
...
must
...
declare
...
and
...
use
...
its
...
instance.
...
Likewise,
...
the
...
UserDetails
...
depends
...
on
...
AccountHolder
...
but
...
instead
...
chooses
...
to
...
extend
...
from it.
| Code Block | ||
|---|---|---|
| ||
it. {mc} Methods in AccountHolder are designed for inheritance and should not be final {mc} {code:bgColor=#ffcccc} package Account; import User.*; public class AccountHolder { private UserDetails ud; // Uses a class defined in package User synchronized void depositFunds(String username, double amount) { // Use a utility method of UserDetails to check whether username exists if (ud.exists(username)) { // Deposit the amount } } protected double getBalance(String accountNumber) { // return the account balance return 1.0; } } {code} {code:bgColor=#ffcccc |
| Code Block | ||
|---|---|---|
| ||
} package User; import Account.*; 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 boolean exists(String username) { // Check whether user exists return true; // Exists } } {code} h2. 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}}. The cyclic dependency is eliminated by ensuring that the {{AccountHolder}} does not use an instance of {{UserDetails}} but instead relies on the interface by importing the {{Bank}} package (and not by implementing the interface). In this compliant solution, such functionality is achieved by adding a parameter of the interface type {{BankApplication}} to the {{depositFunds()}} method. This solution gives the {{AccountHolder}} a solid contract to bank on. Additionally, {{UserDetails}} implements the interface and provides concrete implementations of the methods while at the same time inheriting the other methods from {{AccountHolder}}. {code:bgColor=#ccccff} |
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. The cyclic dependency is eliminated by ensuring that the AccountHolder does not use an instance of UserDetails but instead relies on the interface by importing the Bank package (and not by implementing the interface).
In this compliant solution, such functionality is achieved by adding a parameter of the interface type BankApplication to the depositFunds() method. This solution gives the AccountHolder a solid contract to bank on. Additionally, UserDetails implements the interface and provides concrete implementations of the methods while at the same time inheriting the other methods from AccountHolder.
| Code Block | ||
|---|---|---|
| ||
package Bank;
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 | ||
|---|---|---|
| ||
{code} {code:bgColor=#ccccff} package Account; import Bank.*; // Import from a third package class AccountHolder { 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} {code: |
| Code Block | ||
|---|---|---|
| ||
bgColor=#ccccff} package User; import Account.*; // One way dependency import Bank.*; // 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} {code:bgColor=#ccccff} |
| 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();
}
}
{code}
|
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
...
is
...
that
...
methods
...
declared
...
in
...
the
...
interface
...
are
...
required
...
to
...
be
...
public
...
in
...
the
...
classes
...
that
...
define
...
them.
...
Risk
...
Assessment
...
Cyclic
...
dependencies
...
between
...
packages
...
can
...
result
...
in
...
fragile
...
builds.
...
A
...
security
...
vulnerability
...
in
...
a
...
package
...
can
...
easily
...
percolate
...
to
...
other
...
packages.
...
Guideline | Severity | Likelihood | Remediation Cost | Priority | Level |
|---|---|---|---|---|---|
DCL13-J | low | probable | medium | P4 | L3 |
Bibliography
Chapter 1: "OO Principles and Patterns" | |
| 1.2.5 "Acyclic Dependencies Principle" |
...
DCL12-J. Prevent class initialization cycles 01. Declarations and Initialization (DCL) DCL62-J. Minimize the scope of variables