...
Code Block |
---|
|
public final class BankOperations {
...
|
Noncompliant Code Example
The Javabeans pattern uses a no-argument constructor along with a series of parallel setter methods to build an object. This pattern is not thread-safe and can lead to inconsistent object state. Moreover, it permits another thread to access the object even though it may only be partially initialized (not all required fields are initialized).
Code Block |
---|
|
class UnsafeCurrency {
// total amount requested (required)
private int dollars = -1; // initialize to default value
private int cents = -1; // initialize to default value
// change requested, denomination (optional)
private int quarters = 0;
private int dimes = 0;
private int nickels = 0;
private int pennies = 0;
public UnsafeCurrency() {} // no argument constructor
/* setter methods */
public void setDollar(int amount) { dollars = amount;}
public void setCents(int amount) { cents = amount;}
public void setQuarters(int quantity) { quarters = quantity;}
public void setDimes(int quantity) { dimes = quantity;}
public void setNickels(int quantity) { nickels = quantity;}
public void setPennies(int quantity) { pennies = quantity;}
}
|
Compliant Solution
Wiki Markup |
---|
Use the Builder pattern's \[[Gamma 95|AA. Java References#Gamma 95]\] variant suggested by \[[Bloch 08|AA. Java References#Bloch 08]\] to ensure thread safety and atomicity of object creation. The idea is to call the constructor with the _required_ parameters and obtain a _builder_ object. Each _optional_ parameter can be set using setters on the builder. The object construction concludes with the invocation of the {{build()}} method. The class {{Currency}} also becomes immutable as a result. |
Code Block |
---|
|
class Currency {
// total amount requested (required)
private final int dollars;
private final int cents;
// change requested, denomination (optional)
private final int quarters;
private final int dimes;
private final int nickels;
private final int pennies;
/* Static class member */
private Currency(Builder builder) {
dollars = builder.dollars;
cents = builder.cents;
quarters = builder.quarters;
dimes = builder.dimes;
nickels = builder.nickels;
pennies = builder.pennies;
}
public static class Builder {
private final int dollars;
private final int cents;
private int quarters = 0;
private int dimes = 0;
private int nickels = 0;
private int pennies = 0;
public Builder(int dollars, int cents) {
this.dollars = dollars;
this.cents = cents;
}
public Builder quarters(int quantity) {
quarters = quantity; return this;
}
public Builder dimes(int quantity) {
dimes = quantity; return this;
}
public Builder nickles(int quantity) {
nickels = quantity; return this;
}
public Builder pennies(int quantity) {
pennies = quantity; return this;
}
public Currency build() {
return new Currency(this);
}
}
}
Client code:
Currency USD = new Currency.Builder(100,56).quarters(2).dimes(5).pennies(1).build();
|
If input has to be validated, make sure that the values are copied from the builder class to the containing class's fields prior to checking. The builder class does not violate SCP02-J. Use nested classes carefully since it maintains a copy of the variables defined in the scope of the containing class. These take precedence and thus do not break encapsulation.
Exceptions
EX1: When the API cannot be extended (consider a non-final class, for example), it is permissible to use a flag to signal that object construction succeeded. This is shown below.
Code Block |
---|
|
class BankOperations {
public volatile boolean initialized = false;
public BankOperations() {
if (!performSSNVerification()) {
throw new SecurityException("Invalid SSN!");
}
else
initialized = true; // object construction succeeded
}
private boolean performSSNVerification() {
return false;
}
public void greet() {
if(initialized == true) {
System.out.println("Welcome user! You may now use all the features.");
// ...
}
else
System.out.println("You are not permitted!");
}
}
|
EX2: It is permissible to use the telescoping pattern when the overhead of the builder pattern is significant as compared to the number of parameters required to be initialized. This pattern prescribes a constructor to initialize the required parameters and individual constructors for each optional parameter that is added.
Risk Assessment
Allowing a partially initialized object to be accessed can provide an attacker with an opportunity to exploit the object.
...