Mamy funkcję API, która dzieli całkowitą kwotę na kwoty miesięczne na podstawie danych dat rozpoczęcia i zakończenia.
// 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,
};
}
Ostatnio konsument tego interfejsu API chciał określić zakres dat na inne sposoby: 1) podając liczbę miesięcy zamiast daty końcowej, lub 2) podając miesięczną płatność i obliczając datę końcową. W odpowiedzi zespół API zmienił funkcję na:
// 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),
};
}
Wydaje mi się, że ta zmiana komplikuje interfejs API. Teraz rozmówca musi się martwić o ukryte heurystyki z realizacją danej funkcji w celu ustalenia, które parametry mają pierwszeństwo w wykorzystywane do obliczenia zakres dat (czyli w kolejności priorytetów monthlyPayment
, numMonths
, endDate
). Jeśli osoba dzwoniąca nie zwraca uwagi na podpis funkcji, może wysłać wiele opcjonalnych parametrów i pomylić się, dlaczego endDate
jest ignorowana. Określamy to zachowanie w dokumentacji funkcji.
Dodatkowo uważam, że stanowi zły precedens i dodaje obowiązki do API, którym nie powinien się zajmować (tj. Naruszać SRP). Załóżmy, że dodatkowi klienci chcą, aby funkcja obsługiwała więcej przypadków użycia, takich jak obliczanie total
na podstawie parametrów numMonths
i monthlyPayment
. Ta funkcja z czasem będzie się komplikować.
Preferuję, aby funkcja pozostała taka, jaka była, a zamiast tego wymagać od osoby dzwoniącej obliczenia endDate
siebie. Jednak mogę się mylić i zastanawiałem się, czy wprowadzone przez nich zmiany były akceptowalnym sposobem zaprojektowania funkcji API.
Alternatywnie, czy istnieje wspólny wzorzec obsługi takich scenariuszy? Możemy zapewnić dodatkowe funkcje wyższego rzędu w naszym interfejsie API, które zawijają oryginalną funkcję, ale powoduje to powiększenie interfejsu API. Być może moglibyśmy dodać dodatkowy parametr flagi określający, które podejście należy zastosować w funkcji.
Date
- można dostarczyć łańcuch i może być analizowany w celu ustalenia terminu. Jednak w ten sposób parametry obsługi mogą być również bardzo wybredne i mogą dawać niewiarygodne wyniki. Zobacz Date
jeszcze raz. Właściwie nie jest to niemożliwe - Moment radzi sobie z tym lepiej, ale korzystanie z niego jest bardzo denerwujące.
monthlyPayment
jest podany, ale total
nie jest jego całkowitą wielokrotnością. A także, jak radzić sobie z możliwymi błędami zaokrąglania zmiennoprzecinkowego, jeśli nie można zagwarantować, że wartości są liczbami całkowitymi (np. Spróbuj za pomocą total = 0.3
i monthlyPayment = 0.1
).