обновить поле, когда другие поля мотивированы - PullRequest
1 голос
/ 04 октября 2019

У меня есть класс Installment и метод executeTransaction. Поле totalBalance представляет разницу между общей суммой задолженности и общей суммой выплат. Внутри метода executeTransaction объект рассрочки модифицируется с помощью установщиков. И после каждого сеттера вызывается updateTotalBalance.

    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 BigDecimal totalBalance;

        public void updateTotalBalance() {
             this.totalBalance = this.principalDue.subtract(this.penaltyPaid)
                .add(this.interestDue).subtract(this.interestPaid)
                .add(this.feeDue).subtract(this.feePaid)
                .add(this.penaltyDue).subtract(this.penaltyPaid);
        }

        //seters
        //getters
    }

Метод транзакции:


    public void executeTransaction(Installment installment){
        //code
        installment.setPrincipalPaid(bigDecimalValue);
        installment.updateTotalBalance();
        //code
        installment.setPenaltyDue(bigDecimalValue);
        installment.updateTotalBalance();
    }

Я думал о том, чтобы поместить updateTotalBalance в сеттеры, но для меня оба эти подходакажутся противоречащими лучшим принципам дизайна. Q: Я хочу знать, есть ли лучшие решения для обновления поля в классе, когда модифицируются другие поля.

Ответы [ 2 ]

2 голосов
/ 04 октября 2019

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
    }
}
1 голос
/ 04 октября 2019

Вы можете следовать принципу единственной ответственности , изолируя (делегируя) вычисления в другом объекте и сохраняя Installment в качестве простого POJO.

public class Calculator {

    public BigDecimal balance(Installment installment) {
        return installment.getPrincipalDue().subtract(installment.getPenaltyPaid())
                .add(installment.getInterestDue()).subtract(installment.getInterestPaid())
                .add(installment.getFeeDue()).subtract(installment.getFeePaid())
                .add(installment.getPenaltyDue()).subtract(installment.getPenaltyPaid());
    }
}
import java.math.BigDecimal;

@Value
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;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...