totalBalance
не должно быть нормальным полем, потому что оно составляет Installment
нелогичная структура данных .
нелогичная структура данных - это тот, который по определению может принимать бессмысленные значения.
В вашем случае Installment
является нелогичным , поскольку поле totalBalance
может содержать неправильное значение, поэтому вы должны заменить это поле методом getTotalBalance
.
Если вы не хотите каждый раз пересчитывать totalBalance
, вы можете применить идиома отложенной загрузки . Вот как вы можете это сделать:
public class Installment {
private BigDecimal principalDue;
private BigDecimal principalPaid;
private BigDecimal interestDue;
private BigDecimal interestPaid;
private BigDecimal feeDue;
private BigDecimal feePaid;
private BigDecimal penaltyDue;
private BigDecimal penaltyPaid;
private final ResettableLazyHolder<BigDecimal> totalBalance =
new ResettableLazyHolder<>(this::calculateTotalBalance);
private BigDecimal calculateTotalBalance() {
return principalDue.subtract(penaltyPaid)
.add(interestDue).subtract(interestPaid)
.add(feeDue).subtract(feePaid)
.add(penaltyDue).subtract(penaltyPaid);
}
// Always assign fields with setters even in private context
public void setPrincipalDue(BigDecimal principalDue) {
// add this line to each setter
totalBalance.reset();
this.principalDue = principalDue;
}
public BigDecimal getTotalBalance() {
return totalBalance.get();
}
// other getters and setters
}
Где ResettableLazyHolder
:
public class ResettableLazyHolder<T> {
private boolean initialized = false;
private T value;
private final Supplier<? extends T> initializer;
public ResettableLazyHolder(Supplier<? extends T> initializer) {
this.initializer = initializer;
}
// add synchronized if you need thread-safety
public T get() {
// it's not enough to check that value == null because null can be a valid value
if(!initialized) {
value = initializer.get();
initialized = true;
}
return value;
}
public void reset() {
initialized = false;
value = null; // releases value for GC
}
}