Temos uma função de API que divide um valor total em valores mensais com base nas datas de início e término.
// 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,
};
}
Recentemente, um consumidor dessa API desejou especificar o período de outras maneiras: 1) fornecendo o número de meses em vez da data final ou 2) fornecendo o pagamento mensal e calculando a data final. Em resposta a isso, a equipe da API alterou a função para o seguinte:
// 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),
};
}
Eu sinto que essa mudança complica a API. Agora, o chamador precisa se preocupar com a heurística escondidas com a implementação da função na determinação de quais parâmetros têm preferência em ser usado para calcular o intervalo de datas (ou seja, por ordem de prioridade monthlyPayment
, numMonths
, endDate
). Se um chamador não prestar atenção na assinatura da função, ele poderá enviar vários parâmetros opcionais e ficar confuso sobre o motivo pelo qual endDate
está sendo ignorado. Nós especificamos esse comportamento na documentação da função.
Além disso, acho que ele cria um precedente ruim e adiciona responsabilidades à API com as quais ela não deve se preocupar (ou seja, violar o SRP). Suponha que consumidores adicionais desejem que a função suporte mais casos de uso, como o cálculo total
dos parâmetros numMonths
e monthlyPayment
. Esta função se tornará cada vez mais complicada ao longo do tempo.
Minha preferência é manter a função como era e exigir que o chamador se calcule endDate
. No entanto, posso estar errado e queria saber se as alterações feitas foram uma maneira aceitável de criar uma função de API.
Como alternativa, existe um padrão comum para lidar com cenários como este? Poderíamos fornecer funções adicionais de ordem superior em nossa API que envolvam a função original, mas isso incha a API. Talvez possamos adicionar um parâmetro de flag adicional especificando qual abordagem usar dentro da função.
Date
- você pode fornecer uma corda e ele pode ser analisado para determinar a data. No entanto, desta forma, oh parâmetros de manipulação também podem ser muito exigentes e podem produzir resultados não confiáveis. Veja Date
novamente. Não é impossível fazer o certo - o Moment lida com isso muito melhor, mas é muito chato de usar, independentemente.
monthlyPayment
é dado, mas total
não é um múltiplo inteiro dele. E também como lidar com possíveis erros de arredondamento de ponto flutuante se não for garantido que os valores sejam inteiros (por exemplo, tente com total = 0.3
e monthlyPayment = 0.1
).