Java 我应该如何重构此方法以遵守Bob叔叔规则,即参数不超过2或3个?

Java 我应该如何重构此方法以遵守Bob叔叔规则,即参数不超过2或3个?,java,refactoring,Java,Refactoring,我有一个很重的方法,可以根据多个参数计算合同的利息。我确实需要这些参数中的每一个,但这违反了Bob叔叔的规则,即参数不超过2或3个 public double calculInteretsParPeriode(double encours, double tauxWithMarge, Date dtEch, Date dtEchPrec, boolean isFirstEcheanceInterets, Periode periodiciteK,

我有一个很重的方法,可以根据多个参数计算合同的利息。我确实需要这些参数中的每一个,但这违反了Bob叔叔的规则,即参数不超过2或3个

public double calculInteretsParPeriode(double encours, double tauxWithMarge, Date dtEch, Date dtEchPrec, boolean isFirstEcheanceInterets, Periode periodiciteK,
                                           BaseCalcul baseCalcul, TauxProportionnelOuEquivalent tauxProportionnelOuEquivalent, int tauxNDecimal,
                                           Periode periodiciteI, PeriodeCalculInterets periodeCalculInterets) {

... 

        int nMensualiteAnPlusGrand = CalculMensualite.calculNMensualiteParAnsMax(periodiciteK, periodiciteI);

        double coeffI = CalcEmprunt2014.calcCoeffInterets(
            baseCalcul.getNumerateur(),
            baseCalcul.getDenominateur(),
            dtEch,
            dtEchPrec,
            nMensualiteAnPlusGrand,
            baseCalcul.getProrata(),
            isFirstEcheanceInterets
        );

        //les intérêts sont calculés avec le taux + la marge du contrat
        switch (tauxProportionnelOuEquivalent) {
            case PROPORTIONNEL:
                return MathUtils.round2D(encours * MathUtils.arrondi(tauxWithMarge, tauxNDecimal) / 100.0 * coeffI);
            case EQUIVALENT:
                return MathUtils.round2D(encours * (Math.pow((1 + MathUtils.arrondi(tauxWithMarge, tauxNDecimal) / 100), coeffI) - 1));
            case PROPORTIONNEL_EQUIVALENT:
                return MathUtils.round2D(encours * (MathUtils.arrondi((Math.pow((1 + MathUtils.arrondi(tauxWithMarge, tauxNDecimal) / 100), coeffI) - 1) * nMensualiteAnPlusGrand, tauxNDecimal + 2) * coeffI));
            default:
                throw new GenericRuntimeException("Méthode de calcul des intérêts non pris en charge: " + tauxProportionnelOuEquivalent);
        }
}
我尝试过使用构建器模式,但这看起来像是作弊,因为我只是将所需的变量移动到类字段中,仍然需要将它们传递给构建器,这感觉很混乱:

ew CalculInteretsParPeriodeBuilder()
            .withEncours(10000)
            .withTauxWithMarge(1.0)
            .withDtEch(DateCalculs.getDate(2019, Month.FEBRUARY, 1))
            .withDtEchPrec(DateCalculs.getDate(2018, Month.FEBRUARY, 1))
            .withIsFirstEcheanceInterets(false)
            .withPeriodiciteI(Periode.ANNUELLE)
            .withPeriodiciteK(Periode.ANNUELLE)
            .withTauxNDecimal(2)
            .withPeriodeCalculInterets(PeriodeCalculInterets.PAR_PERIODE)
            .withBaseCalcul(BaseCalcul.BC_360360)
            .withTauxProportionnelOuEquivalent(TauxProportionnelOuEquivalent.EQUIVALENT)
            .build()
            .calculInteretsParPeriode()

当您有多个可交换(可选)的参数时,生成器模式非常有用。然后您可以将它们链接在一起并配置您的对象。但最终他们只初始化了一个对象。因此,如果您有20个参数,并且您的对象总是需要同样的10个参数才能正常工作,那么将其放入构建器模式是不值得的

让我们假设这是最简单的:

new CalculInteretsParPeriodeBuilder()
        .withEncours(10000)
        .withTauxWithMarge(1.0)
        .withDtEch(DateCalculs.getDate(2019, Month.FEBRUARY, 1))
        .withDtEchPrec(DateCalculs.getDate(2018, Month.FEBRUARY, 1))
        .withIsFirstEcheanceInterets(false);
那么只调用构造函数就更短了:

CalculInteretsParameters params = new CalculInteretsParameters(
        10000,
        1.0,
        DateCalculs.getDate(2019, Month.FEBRUARY, 1),
        DateCalculs.getDate(2018, Month.FEBRUARY, 1),
        false
);
在我看来,一些简单的
setter
调用所有可选参数没有什么错:

 params.setPeriodiciteI(Periode.ANNUELLE);
 params.setPeriodiciteK(Periode.ANNUELLE);

 if(reallyNecessary){           // see? way more dynamic :)
     params.setTauxNDecimal(2);
     params.setPeriodeCalculInterets(PeriodeCalculInterets.PAR_PERIODE);
 }else{
     params.setBaseCalcul(BaseCalcul.BC_360360);
 }
对你来说,我不会用它。只需初始化一个参数对象(根据需要动态地)并将其传递给计算方法

编辑

抱歉,要回答您的问题,我将使用参数类:

 CalculInteretsParameters params = new CalculInteretsParameters();
 params.setTauxWithMarge(1.0);  //  set all your dates, numbers and parameters
 params.setPeriodicite(2);
 ...

 // then call your method (maybe even with some dynamic values)
 calculInteretsParPeriode(params, someDynamicValue);

为了展示一些代码,我使用了一个生成器作为“常规”参数,并在calcul()方法的签名中使用了更多的“特定”参数,从而完成了@Gamedroid建议的工作


所以非常感谢

您可以创建一个单独的类来存储值,比如说,
MyCalculationParameters
class。然后将这一个对象传递到方法中。他们已经是一个“巨大”的对象,包含了计算所需的所有参数,但这是我的整个“合同”,它有150多个字段。我们过去只是将契约传递给所有方法,但对于像这样的一些用例来说并不方便,在这些用例中,这个方法需要从不同的上下文中调用,而我们没有契约对象。是的,150个字段相当多。你不能把它们组合在一起,创建“子对象”,然后你可以用它来调用不同的方法吗?因此,保留“Contract”类,而不是
DtEch
DtEchPrec
DtEchProch
DtEchFin
。。。。您创建了一个类
Echeances
,在其中放置所有这些日期、一些其他相关值,并将该对象传递给您的合同。与
Taux
等相关的所有参数都是一样的。是的,实际上在所有这些参数中,它们都是合同本身的一部分:-PeriodiciteI-PeriodiciteK-TauxNDecimal-periodicalculinterets-BaseCalcul-tauxproportionelouequivalent,可以重新组合,其他参数是动态字段,具体取决于我的技术计算interets。这肯定是我应该在方法中将“一般契约”参数与动态参数分开的东西,因此我应该将所有这些参数从我的实际方法
calculInteretsParPeriode
传递到容器对象
CalculInteretsParameters
?要使用所有参数构建此容器,请使用Factory或Builder模式?我只是觉得我是在回避问题,而不是面对它。不过,我看不出有其他/更好的方法来做这件事。。如何更好地使用参数类而不是将所有参数直接传递给方法?可以重用参数类。或者最好是参数对象。重新计算并更新其值,然后继续。无需从计算方法返回它,也无需将所有参数再次传递给其他方法。但我不认为有一个通用规则可以应用于“清理”方法调用。如果有一个特定的方法真的需要50个参数才能工作,那么这并不是一切的结束。只要不是所有方法都有这么多参数,只要不传递不需要的值,我就应该尝试实现将常规参数与动态参数分离的好主意。可能使用包含所有常规参数的构造函数的BuilderPattern,并使用setter添加动态参数。
public class CalculInteretsParPeriodeBuilder {
    private ContratBasic contratBasic;
    private DefinitionTaux contratTaux;
    private Periode periodiciteK;
    private Periode periodiciteI;
    private BaseCalcul baseCalcul;
    private TauxProportionnelOuEquivalent tauxProportionnelOuEquivalent;
    private Integer tauxNDecimal;
    private PeriodeCalculInterets periodeCalculInterets = PeriodeCalculInterets.PAR_PERIODE;

    public CalculInteretsParPeriodeBuilder withContratBasic(ContratBasic contratBasic) {
        this.contratBasic = contratBasic;
        return this;
    }

    public CalculInteretsParPeriodeBuilder withContratTaux(DefinitionTaux contratTaux) {
        this.contratTaux = contratTaux;
        return this;
    }

    public CalculInteretsParPeriodeBuilder withPeriodiciteK(Periode periodiciteK) {
        this.periodiciteK = periodiciteK;
        return this;
    }

    public CalculInteretsParPeriodeBuilder withBaseCalcul(BaseCalcul baseCalcul) {
        this.baseCalcul = baseCalcul;
        return this;
    }

    public CalculInteretsParPeriodeBuilder withBaseCalcul(com.dev1.seldon.infodette.beans.contrat2014.commons.enums.BaseCalcul baseCalcul) {
        this.baseCalcul = new BaseCalcul(baseCalcul);
        return this;
    }

    public CalculInteretsParPeriodeBuilder withTauxProportionnelOuEquivalent(TauxProportionnelOuEquivalent tauxProportionnelOuEquivalent) {
        this.tauxProportionnelOuEquivalent = tauxProportionnelOuEquivalent;
        return this;
    }

    public CalculInteretsParPeriodeBuilder withTauxNDecimal(int tauxNDecimal) {
        this.tauxNDecimal = tauxNDecimal;
        return this;
    }

    public CalculInteretsParPeriodeBuilder withPeriodiciteI(Periode periodiciteI) {
        this.periodiciteI = periodiciteI;
        return this;
    }

    public CalculInteretsParPeriodeBuilder withPeriodeCalculInterets(PeriodeCalculInterets periodeCalculInterets) {
        this.periodeCalculInterets = periodeCalculInterets;
        return this;
    }

    public CalculInteretsParPeriode build() {

        if (contratBasic != null && contratTaux != null) {
            this.periodiciteK = contratBasic.getPeriodiciteK();
            this.periodiciteI = contratTaux.getPeriodiciteI();
            this.baseCalcul = contratTaux.getBaseCalculI();
            this.tauxProportionnelOuEquivalent = contratTaux.getTauxProportionnelOuEquivalent();
            this.tauxNDecimal = contratTaux.getTauxNDecimal();
            this.periodeCalculInterets = contratTaux.getPeriodeCalculInteret();
        }

        if (periodiciteK == null) throw new MandatoryMethodParameterException("periodiciteK");
        if (baseCalcul == null) throw new MandatoryMethodParameterException("baseCalcul");
        if (tauxProportionnelOuEquivalent == null) throw new MandatoryMethodParameterException("tauxProportionnelOuEquivalent");
        if (tauxNDecimal == null) throw new MandatoryMethodParameterException("tauxNDecimal");
        if (periodiciteI == null) throw new MandatoryMethodParameterException("periodiciteI");

        return new CalculInteretsParPeriode(periodiciteK, baseCalcul, tauxProportionnelOuEquivalent, tauxNDecimal, periodiciteI, periodeCalculInterets);
    }
}
public class CalculInteretsParPeriode {

    private PeriodeCalculInterets periodeCalculInterets;
    private Periode periodiciteI;
    private Periode periodiciteK;
    private TauxProportionnelOuEquivalent tauxProportionnelOuEquivalent;
    private int tauxNDecimal;
    private BaseCalcul baseCalcul;

    public CalculInteretsParPeriode(Periode periodiciteK, BaseCalcul baseCalcul, TauxProportionnelOuEquivalent tauxProportionnelOuEquivalent, int tauxNDecimal,
                                    Periode periodiciteI, PeriodeCalculInterets periodeCalculInterets) {
        this.periodiciteK = periodiciteK;
        this.periodiciteI = periodiciteI;
        this.baseCalcul = baseCalcul;
        this.tauxProportionnelOuEquivalent = tauxProportionnelOuEquivalent;
        this.tauxNDecimal = tauxNDecimal;
        this.periodeCalculInterets = periodeCalculInterets;
    }

    public double calculInteretsParPeriode(double encours, double tauxWithMarge, Date dtEch, Date dtEchPrec, boolean isFirstEcheanceInterets) {

        if (dtEch == null) throw new MandatoryMethodParameterException("dtEch");
        if (dtEchPrec == null) throw new MandatoryMethodParameterException("dtEchPrec");

        int nMensualiteAnPlusGrand = CalculMensualite.calculNMensualiteParAnsMax(periodiciteK, periodiciteI);

        double coeffI = CalcEmprunt2014.calcCoeffInterets(
            baseCalcul.getNumerateur(),
            baseCalcul.getDenominateur(),
            dtEch,
            dtEchPrec,
            nMensualiteAnPlusGrand,
            baseCalcul.getProrata(),
            isFirstEcheanceInterets
        );

        return MathUtils.round2D(encours * MathUtils.arrondi(tauxWithMarge, tauxNDecimal) / 100.0 * coeffI);

    }

}