指定された開始日と終了日に基づいて、合計金額を月額に分割するAPI関数があります。
// JavaScript
function convertToMonths(timePeriod) {
// ... returns the given time period converted to months
}
function getPaymentBreakdown(total, startDate, endDate) {
const numMonths = convertToMonths(endDate - startDate);
return {
numMonths,
monthlyPayment: total / numMonths,
};
}
最近、このAPIの消費者は、1)終了日ではなく月数を提供するか、2)毎月の支払いを提供して終了日を計算することにより、他の方法で日付範囲を指定したいと考えました。これに応じて、APIチームは機能を次のように変更しました。
// JavaScript
function addMonths(date, numMonths) {
// ... returns a new date numMonths after date
}
function getPaymentBreakdown(
total,
startDate,
endDate /* optional */,
numMonths /* optional */,
monthlyPayment /* optional */,
) {
let innerNumMonths;
if (monthlyPayment) {
innerNumMonths = total / monthlyPayment;
} else if (numMonths) {
innerNumMonths = numMonths;
} else {
innerNumMonths = convertToMonths(endDate - startDate);
}
return {
numMonths: innerNumMonths,
monthlyPayment: total / innerNumMonths,
endDate: addMonths(startDate, innerNumMonths),
};
}
この変更がAPIを複雑にしていると感じています。今、呼び出し側は(優先順位によって、すなわちパラメータが日付範囲を計算するのに使用されている中で優先順位を取るかを決定するには、関数の実装に隠された経験則を心配する必要がありmonthlyPayment
、numMonths
、endDate
)。呼び出し元が関数シグネチャに注意を払わない場合、複数のオプションのパラメーターを送信し、なぜendDate
無視されているのかについて混乱する可能性があります。関数のドキュメントでこの動作を指定します。
さらに、私はそれが悪い先例を設定し、それ自体に関係してはならない(つまりSRPに違反する)APIに責任を追加すると感じます。追加のコンシューマーtotal
が、numMonths
およびからの計算など、より多くのユースケースをサポートする関数を必要としているとしmonthlyPayment
ます。この関数は、時間とともにますます複雑になります。
私の好みは、関数をそのままにしておき、代わりに呼び出し元にendDate
自分自身を計算させることです。しかし、私は間違っている可能性があり、それらが行った変更がAPI関数を設計するための許容可能な方法であるかどうか疑問に思っていました。
あるいは、このようなシナリオを処理するための一般的なパターンはありますか?APIで元の関数をラップする追加の高次関数を提供できますが、これはAPIを膨張させます。関数内で使用するアプローチを指定するフラグパラメーターを追加できます。
monthlyPayment
は、が与えられているがtotal
整数倍ではない場合の処理方法を考えたいかもしれません。また、値が整数であることが保証されていない場合に発生する可能性のある浮動小数点丸めエラーに対処する方法(例:total = 0.3
およびmonthlyPayment = 0.1
)。