我应该如何重构此方法以遵守不超过 2 或 3 个参数的 Uncle 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()

为了展示一些代码,我最终完全按照@GameDroids建议的方式使用构建器对"常规"参数进行操作,并在 calcul(( 方法的签名中具有更"特定"的参数。

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);
}
}

所以非常感谢

当您有多个可交换(和可选(的参数时,生成器模式很有用。然后,您可以将它们链接在一起并配置对象。但最终他们只初始化一个对象。因此,如果你有 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);