Is there a pattern for handling conflicting function parameters?How do you deal with errors in enumeration /...
How does Monks' Improved Unarmored Movement work out of combat?
Delete n lines skip 1 line script
Was the ruling that prorogation was unlawful only possible because of the creation of a separate supreme court?
How to study endgames?
Verb ending in -ん with positive meaning?
Is there an in-universe explanation of how Frodo's arrival in Valinor was recorded in the Red Book?
IEEE 754 square root with Newton-Raphson
A word that refers to saying something in an attempt to anger or embarrass someone into doing something that they don’t want to do?
Can adverbs modify adjectives?
does 'java' command compile the java program?
Is it good to engage in exceptional cases where it is permissible to do a typically forbidden action to which one has a taivah for
Why has Speaker Pelosi been so hesitant to impeach President Trump?
Can an energy drink or chocolate before an exam be useful ? What sort of other edible goods be helpful?
Beyond Futuristic Technology for an Alien Warship?
Is it possible to take a database offline when doing a backup using an SQL job?
Is there any site with telescopes data?
Phonetic distortion when words are borrowed among languages
Should I be an author on another PhD student's paper if I went to their meetings and gave advice?
French license plates
Incomplete iffalse: How to shift a scope in polar coordinate?
Avoiding dust scattering when you drill
Writing a program that will filter the integer solutions
Which Catholic priests were given diplomatic missions?
Garage door sticks on a bolt
Is there a pattern for handling conflicting function parameters?
How do you deal with errors in enumeration / list processing (lowish-level API)As an API-user, would you tolerate BestPractice Exceptions?When writing a library or an API, when should and when shouldn't I validate or automatically correct errors in data provided by another developer?Idiomatic wrapping of C++ template type API in CCollection properties and initializer lists in .Net API designWhat HTTP action and return value should be used on resource's actionHandling Different Parameters for Derived Classes
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty{ margin-bottom:0;
}
We have an API function that breaks down a total amount into monthly amounts based on given start and end dates.
// 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,
};
}
Recently, a consumer for this API wanted to specify the date range in other ways: 1) by providing the number of months instead of the end date, or 2) by providing the monthly payment and calculating the end date. In response to this, the API team changed the function to the following:
// 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),
};
}
I feel this change complicates the API. Now the caller needs to worry about the heuristics hidden with the function's implementation in determining which parameters take preference in being used to calculate the date range (i.e. by order of priority monthlyPayment
, numMonths
, endDate
). If a caller doesn't pay attention to the function signature, they might send multiple of the optional parameters and get confused as to why endDate
is being ignored. We do specify this behavior in the function documentation.
Additionally I feel it sets a bad precedent and adds responsibilities to the API that it should not concern itself with (i.e. violating SRP). Suppose additional consumers want the function to support more use cases, such as calculating total
from the numMonths
and monthlyPayment
parameters. This function will become more and more complicated over time.
My preference is to keep the function as it was and instead require the caller to calculate endDate
themselves. However, I may be wrong and was wondering if the changes they made were an acceptable way to design an API function.
Alternatively, is there a common pattern for handling scenarios like this? We could provide additional higher-order functions in our API that wrap the original function, but this bloats the API. Maybe we could add an additional flag parameter specifying which approach to use inside of the function.
api-design
add a comment
|
We have an API function that breaks down a total amount into monthly amounts based on given start and end dates.
// 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,
};
}
Recently, a consumer for this API wanted to specify the date range in other ways: 1) by providing the number of months instead of the end date, or 2) by providing the monthly payment and calculating the end date. In response to this, the API team changed the function to the following:
// 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),
};
}
I feel this change complicates the API. Now the caller needs to worry about the heuristics hidden with the function's implementation in determining which parameters take preference in being used to calculate the date range (i.e. by order of priority monthlyPayment
, numMonths
, endDate
). If a caller doesn't pay attention to the function signature, they might send multiple of the optional parameters and get confused as to why endDate
is being ignored. We do specify this behavior in the function documentation.
Additionally I feel it sets a bad precedent and adds responsibilities to the API that it should not concern itself with (i.e. violating SRP). Suppose additional consumers want the function to support more use cases, such as calculating total
from the numMonths
and monthlyPayment
parameters. This function will become more and more complicated over time.
My preference is to keep the function as it was and instead require the caller to calculate endDate
themselves. However, I may be wrong and was wondering if the changes they made were an acceptable way to design an API function.
Alternatively, is there a common pattern for handling scenarios like this? We could provide additional higher-order functions in our API that wrap the original function, but this bloats the API. Maybe we could add an additional flag parameter specifying which approach to use inside of the function.
api-design
1
"Recently, a consumer for this API wanted to [provide] the number of months instead of the end date" - This is a frivolous request. They can transform the # of months into a proper end date in a line or two of code on their end.
– Graham
7 hours ago
add a comment
|
We have an API function that breaks down a total amount into monthly amounts based on given start and end dates.
// 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,
};
}
Recently, a consumer for this API wanted to specify the date range in other ways: 1) by providing the number of months instead of the end date, or 2) by providing the monthly payment and calculating the end date. In response to this, the API team changed the function to the following:
// 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),
};
}
I feel this change complicates the API. Now the caller needs to worry about the heuristics hidden with the function's implementation in determining which parameters take preference in being used to calculate the date range (i.e. by order of priority monthlyPayment
, numMonths
, endDate
). If a caller doesn't pay attention to the function signature, they might send multiple of the optional parameters and get confused as to why endDate
is being ignored. We do specify this behavior in the function documentation.
Additionally I feel it sets a bad precedent and adds responsibilities to the API that it should not concern itself with (i.e. violating SRP). Suppose additional consumers want the function to support more use cases, such as calculating total
from the numMonths
and monthlyPayment
parameters. This function will become more and more complicated over time.
My preference is to keep the function as it was and instead require the caller to calculate endDate
themselves. However, I may be wrong and was wondering if the changes they made were an acceptable way to design an API function.
Alternatively, is there a common pattern for handling scenarios like this? We could provide additional higher-order functions in our API that wrap the original function, but this bloats the API. Maybe we could add an additional flag parameter specifying which approach to use inside of the function.
api-design
We have an API function that breaks down a total amount into monthly amounts based on given start and end dates.
// 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,
};
}
Recently, a consumer for this API wanted to specify the date range in other ways: 1) by providing the number of months instead of the end date, or 2) by providing the monthly payment and calculating the end date. In response to this, the API team changed the function to the following:
// 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),
};
}
I feel this change complicates the API. Now the caller needs to worry about the heuristics hidden with the function's implementation in determining which parameters take preference in being used to calculate the date range (i.e. by order of priority monthlyPayment
, numMonths
, endDate
). If a caller doesn't pay attention to the function signature, they might send multiple of the optional parameters and get confused as to why endDate
is being ignored. We do specify this behavior in the function documentation.
Additionally I feel it sets a bad precedent and adds responsibilities to the API that it should not concern itself with (i.e. violating SRP). Suppose additional consumers want the function to support more use cases, such as calculating total
from the numMonths
and monthlyPayment
parameters. This function will become more and more complicated over time.
My preference is to keep the function as it was and instead require the caller to calculate endDate
themselves. However, I may be wrong and was wondering if the changes they made were an acceptable way to design an API function.
Alternatively, is there a common pattern for handling scenarios like this? We could provide additional higher-order functions in our API that wrap the original function, but this bloats the API. Maybe we could add an additional flag parameter specifying which approach to use inside of the function.
api-design
api-design
edited 8 hours ago
CalMlynarczyk
asked 8 hours ago
CalMlynarczykCalMlynarczyk
1144 bronze badges
1144 bronze badges
1
"Recently, a consumer for this API wanted to [provide] the number of months instead of the end date" - This is a frivolous request. They can transform the # of months into a proper end date in a line or two of code on their end.
– Graham
7 hours ago
add a comment
|
1
"Recently, a consumer for this API wanted to [provide] the number of months instead of the end date" - This is a frivolous request. They can transform the # of months into a proper end date in a line or two of code on their end.
– Graham
7 hours ago
1
1
"Recently, a consumer for this API wanted to [provide] the number of months instead of the end date" - This is a frivolous request. They can transform the # of months into a proper end date in a line or two of code on their end.
– Graham
7 hours ago
"Recently, a consumer for this API wanted to [provide] the number of months instead of the end date" - This is a frivolous request. They can transform the # of months into a proper end date in a line or two of code on their end.
– Graham
7 hours ago
add a comment
|
3 Answers
3
active
oldest
votes
Additionally I feel it sets a bad precedent and adds responsibilities to the API that it should not concern itself with (i.e. violating SRP). Suppose additional consumers want the function to support more use cases, such as calculating
total
from thenumMonths
andmonthlyPayment
parameters. This function will become more and more complicated over time.
You're exactly correct.
My preference is to keep the function as it was and instead require the caller to calculate endDate themselves. However, I may be wrong and was wondering if the changes they made were an acceptable way to design an API function.
This isn't ideal either, because the caller code will be polluted with unrelated boiler plate.
Alternatively, is there a common pattern for handling scenarios like this?
Introduce a new type, like DateInterval
. Add whatever constructors make sense (start date + end date, start date + num months, whatever.). Adopt this as the common-currency types for expressing intervals of dates/times throughout your system.
Are you aware that in a weakly typed language like JavaScript a constructorDateTime(startDate,endDate)
cannot be distinguished from a constructorDateTime(startDate,numOfMonths)
?
– Doc Brown
7 hours ago
1
@DocBrown Yep. In such cases (Ruby, Python, JS), it's customary to just use static/class methods. But that's an implementation detail, that I don't think is particularly relevant to my answer's point ("use a type").
– Alexander
6 hours ago
add a comment
|
Seeing the implementation, it appears to me what you really require here is 3 different functions instead of one:
The original one:
function getPaymentBreakdown(total, startDate, endDate)
The one providing the number of months instead of the end date:
function getPaymentBreakdownByNoOfMonths(total, startDate, noOfMonths)
and the one providing the monthly payment and calculating the end date:
function getPaymentBreakdownByMonthlyPayment(total, startDate, monthlyPayment)
Now, there are no optional parameters any more, and it should be pretty clear which function is called how and for which purpose. Note the different function names don't mean you have to repeat any logic - internally, if these 3 functions share some common logic, it should be refactored to a "private" function.
is there a common pattern for handling scenarios like this
I don't think there is a "pattern" (in the sense of the GoF design patterns) which describes good API design. Using self-describing names, functions with fewer parameters, functions with orthogonal (=independent) parameters, are just basic principles of creating readable, maintainable and evolvable code. Not every good idea in programming is necesarily a "design pattern".
add a comment
|
Sometimes fluent-expressions help on this:
let payment1 = forTotalAmount(1234)
.breakIntoPayments()
.byPeriod(months(2));
let payment2 = forTotalAmount(1234)
.breakIntoPayments()
.byDateRange(saleStart, saleEnd);
let monthsDue = forTotalAmount(1234)
.calculatePeriod()
.withPaymentsOf(12.34)
.monthly();
Given enough time to design, you can come up with a solid API that acts similar to a domain-specific-language.
The other big advantage is that IDEs with autocomplete make almost irrevelant to read the API documentation, as is intuitive due its self-discoverable capabilities.
add a comment
|
Your Answer
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "131"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/4.0/"u003ecc by-sa 4.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fsoftwareengineering.stackexchange.com%2fquestions%2f398828%2fis-there-a-pattern-for-handling-conflicting-function-parameters%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
3 Answers
3
active
oldest
votes
3 Answers
3
active
oldest
votes
active
oldest
votes
active
oldest
votes
Additionally I feel it sets a bad precedent and adds responsibilities to the API that it should not concern itself with (i.e. violating SRP). Suppose additional consumers want the function to support more use cases, such as calculating
total
from thenumMonths
andmonthlyPayment
parameters. This function will become more and more complicated over time.
You're exactly correct.
My preference is to keep the function as it was and instead require the caller to calculate endDate themselves. However, I may be wrong and was wondering if the changes they made were an acceptable way to design an API function.
This isn't ideal either, because the caller code will be polluted with unrelated boiler plate.
Alternatively, is there a common pattern for handling scenarios like this?
Introduce a new type, like DateInterval
. Add whatever constructors make sense (start date + end date, start date + num months, whatever.). Adopt this as the common-currency types for expressing intervals of dates/times throughout your system.
Are you aware that in a weakly typed language like JavaScript a constructorDateTime(startDate,endDate)
cannot be distinguished from a constructorDateTime(startDate,numOfMonths)
?
– Doc Brown
7 hours ago
1
@DocBrown Yep. In such cases (Ruby, Python, JS), it's customary to just use static/class methods. But that's an implementation detail, that I don't think is particularly relevant to my answer's point ("use a type").
– Alexander
6 hours ago
add a comment
|
Additionally I feel it sets a bad precedent and adds responsibilities to the API that it should not concern itself with (i.e. violating SRP). Suppose additional consumers want the function to support more use cases, such as calculating
total
from thenumMonths
andmonthlyPayment
parameters. This function will become more and more complicated over time.
You're exactly correct.
My preference is to keep the function as it was and instead require the caller to calculate endDate themselves. However, I may be wrong and was wondering if the changes they made were an acceptable way to design an API function.
This isn't ideal either, because the caller code will be polluted with unrelated boiler plate.
Alternatively, is there a common pattern for handling scenarios like this?
Introduce a new type, like DateInterval
. Add whatever constructors make sense (start date + end date, start date + num months, whatever.). Adopt this as the common-currency types for expressing intervals of dates/times throughout your system.
Are you aware that in a weakly typed language like JavaScript a constructorDateTime(startDate,endDate)
cannot be distinguished from a constructorDateTime(startDate,numOfMonths)
?
– Doc Brown
7 hours ago
1
@DocBrown Yep. In such cases (Ruby, Python, JS), it's customary to just use static/class methods. But that's an implementation detail, that I don't think is particularly relevant to my answer's point ("use a type").
– Alexander
6 hours ago
add a comment
|
Additionally I feel it sets a bad precedent and adds responsibilities to the API that it should not concern itself with (i.e. violating SRP). Suppose additional consumers want the function to support more use cases, such as calculating
total
from thenumMonths
andmonthlyPayment
parameters. This function will become more and more complicated over time.
You're exactly correct.
My preference is to keep the function as it was and instead require the caller to calculate endDate themselves. However, I may be wrong and was wondering if the changes they made were an acceptable way to design an API function.
This isn't ideal either, because the caller code will be polluted with unrelated boiler plate.
Alternatively, is there a common pattern for handling scenarios like this?
Introduce a new type, like DateInterval
. Add whatever constructors make sense (start date + end date, start date + num months, whatever.). Adopt this as the common-currency types for expressing intervals of dates/times throughout your system.
Additionally I feel it sets a bad precedent and adds responsibilities to the API that it should not concern itself with (i.e. violating SRP). Suppose additional consumers want the function to support more use cases, such as calculating
total
from thenumMonths
andmonthlyPayment
parameters. This function will become more and more complicated over time.
You're exactly correct.
My preference is to keep the function as it was and instead require the caller to calculate endDate themselves. However, I may be wrong and was wondering if the changes they made were an acceptable way to design an API function.
This isn't ideal either, because the caller code will be polluted with unrelated boiler plate.
Alternatively, is there a common pattern for handling scenarios like this?
Introduce a new type, like DateInterval
. Add whatever constructors make sense (start date + end date, start date + num months, whatever.). Adopt this as the common-currency types for expressing intervals of dates/times throughout your system.
answered 8 hours ago
AlexanderAlexander
1,3278 silver badges16 bronze badges
1,3278 silver badges16 bronze badges
Are you aware that in a weakly typed language like JavaScript a constructorDateTime(startDate,endDate)
cannot be distinguished from a constructorDateTime(startDate,numOfMonths)
?
– Doc Brown
7 hours ago
1
@DocBrown Yep. In such cases (Ruby, Python, JS), it's customary to just use static/class methods. But that's an implementation detail, that I don't think is particularly relevant to my answer's point ("use a type").
– Alexander
6 hours ago
add a comment
|
Are you aware that in a weakly typed language like JavaScript a constructorDateTime(startDate,endDate)
cannot be distinguished from a constructorDateTime(startDate,numOfMonths)
?
– Doc Brown
7 hours ago
1
@DocBrown Yep. In such cases (Ruby, Python, JS), it's customary to just use static/class methods. But that's an implementation detail, that I don't think is particularly relevant to my answer's point ("use a type").
– Alexander
6 hours ago
Are you aware that in a weakly typed language like JavaScript a constructor
DateTime(startDate,endDate)
cannot be distinguished from a constructor DateTime(startDate,numOfMonths)
?– Doc Brown
7 hours ago
Are you aware that in a weakly typed language like JavaScript a constructor
DateTime(startDate,endDate)
cannot be distinguished from a constructor DateTime(startDate,numOfMonths)
?– Doc Brown
7 hours ago
1
1
@DocBrown Yep. In such cases (Ruby, Python, JS), it's customary to just use static/class methods. But that's an implementation detail, that I don't think is particularly relevant to my answer's point ("use a type").
– Alexander
6 hours ago
@DocBrown Yep. In such cases (Ruby, Python, JS), it's customary to just use static/class methods. But that's an implementation detail, that I don't think is particularly relevant to my answer's point ("use a type").
– Alexander
6 hours ago
add a comment
|
Seeing the implementation, it appears to me what you really require here is 3 different functions instead of one:
The original one:
function getPaymentBreakdown(total, startDate, endDate)
The one providing the number of months instead of the end date:
function getPaymentBreakdownByNoOfMonths(total, startDate, noOfMonths)
and the one providing the monthly payment and calculating the end date:
function getPaymentBreakdownByMonthlyPayment(total, startDate, monthlyPayment)
Now, there are no optional parameters any more, and it should be pretty clear which function is called how and for which purpose. Note the different function names don't mean you have to repeat any logic - internally, if these 3 functions share some common logic, it should be refactored to a "private" function.
is there a common pattern for handling scenarios like this
I don't think there is a "pattern" (in the sense of the GoF design patterns) which describes good API design. Using self-describing names, functions with fewer parameters, functions with orthogonal (=independent) parameters, are just basic principles of creating readable, maintainable and evolvable code. Not every good idea in programming is necesarily a "design pattern".
add a comment
|
Seeing the implementation, it appears to me what you really require here is 3 different functions instead of one:
The original one:
function getPaymentBreakdown(total, startDate, endDate)
The one providing the number of months instead of the end date:
function getPaymentBreakdownByNoOfMonths(total, startDate, noOfMonths)
and the one providing the monthly payment and calculating the end date:
function getPaymentBreakdownByMonthlyPayment(total, startDate, monthlyPayment)
Now, there are no optional parameters any more, and it should be pretty clear which function is called how and for which purpose. Note the different function names don't mean you have to repeat any logic - internally, if these 3 functions share some common logic, it should be refactored to a "private" function.
is there a common pattern for handling scenarios like this
I don't think there is a "pattern" (in the sense of the GoF design patterns) which describes good API design. Using self-describing names, functions with fewer parameters, functions with orthogonal (=independent) parameters, are just basic principles of creating readable, maintainable and evolvable code. Not every good idea in programming is necesarily a "design pattern".
add a comment
|
Seeing the implementation, it appears to me what you really require here is 3 different functions instead of one:
The original one:
function getPaymentBreakdown(total, startDate, endDate)
The one providing the number of months instead of the end date:
function getPaymentBreakdownByNoOfMonths(total, startDate, noOfMonths)
and the one providing the monthly payment and calculating the end date:
function getPaymentBreakdownByMonthlyPayment(total, startDate, monthlyPayment)
Now, there are no optional parameters any more, and it should be pretty clear which function is called how and for which purpose. Note the different function names don't mean you have to repeat any logic - internally, if these 3 functions share some common logic, it should be refactored to a "private" function.
is there a common pattern for handling scenarios like this
I don't think there is a "pattern" (in the sense of the GoF design patterns) which describes good API design. Using self-describing names, functions with fewer parameters, functions with orthogonal (=independent) parameters, are just basic principles of creating readable, maintainable and evolvable code. Not every good idea in programming is necesarily a "design pattern".
Seeing the implementation, it appears to me what you really require here is 3 different functions instead of one:
The original one:
function getPaymentBreakdown(total, startDate, endDate)
The one providing the number of months instead of the end date:
function getPaymentBreakdownByNoOfMonths(total, startDate, noOfMonths)
and the one providing the monthly payment and calculating the end date:
function getPaymentBreakdownByMonthlyPayment(total, startDate, monthlyPayment)
Now, there are no optional parameters any more, and it should be pretty clear which function is called how and for which purpose. Note the different function names don't mean you have to repeat any logic - internally, if these 3 functions share some common logic, it should be refactored to a "private" function.
is there a common pattern for handling scenarios like this
I don't think there is a "pattern" (in the sense of the GoF design patterns) which describes good API design. Using self-describing names, functions with fewer parameters, functions with orthogonal (=independent) parameters, are just basic principles of creating readable, maintainable and evolvable code. Not every good idea in programming is necesarily a "design pattern".
edited 7 hours ago
answered 7 hours ago
Doc BrownDoc Brown
143k25 gold badges267 silver badges423 bronze badges
143k25 gold badges267 silver badges423 bronze badges
add a comment
|
add a comment
|
Sometimes fluent-expressions help on this:
let payment1 = forTotalAmount(1234)
.breakIntoPayments()
.byPeriod(months(2));
let payment2 = forTotalAmount(1234)
.breakIntoPayments()
.byDateRange(saleStart, saleEnd);
let monthsDue = forTotalAmount(1234)
.calculatePeriod()
.withPaymentsOf(12.34)
.monthly();
Given enough time to design, you can come up with a solid API that acts similar to a domain-specific-language.
The other big advantage is that IDEs with autocomplete make almost irrevelant to read the API documentation, as is intuitive due its self-discoverable capabilities.
add a comment
|
Sometimes fluent-expressions help on this:
let payment1 = forTotalAmount(1234)
.breakIntoPayments()
.byPeriod(months(2));
let payment2 = forTotalAmount(1234)
.breakIntoPayments()
.byDateRange(saleStart, saleEnd);
let monthsDue = forTotalAmount(1234)
.calculatePeriod()
.withPaymentsOf(12.34)
.monthly();
Given enough time to design, you can come up with a solid API that acts similar to a domain-specific-language.
The other big advantage is that IDEs with autocomplete make almost irrevelant to read the API documentation, as is intuitive due its self-discoverable capabilities.
add a comment
|
Sometimes fluent-expressions help on this:
let payment1 = forTotalAmount(1234)
.breakIntoPayments()
.byPeriod(months(2));
let payment2 = forTotalAmount(1234)
.breakIntoPayments()
.byDateRange(saleStart, saleEnd);
let monthsDue = forTotalAmount(1234)
.calculatePeriod()
.withPaymentsOf(12.34)
.monthly();
Given enough time to design, you can come up with a solid API that acts similar to a domain-specific-language.
The other big advantage is that IDEs with autocomplete make almost irrevelant to read the API documentation, as is intuitive due its self-discoverable capabilities.
Sometimes fluent-expressions help on this:
let payment1 = forTotalAmount(1234)
.breakIntoPayments()
.byPeriod(months(2));
let payment2 = forTotalAmount(1234)
.breakIntoPayments()
.byDateRange(saleStart, saleEnd);
let monthsDue = forTotalAmount(1234)
.calculatePeriod()
.withPaymentsOf(12.34)
.monthly();
Given enough time to design, you can come up with a solid API that acts similar to a domain-specific-language.
The other big advantage is that IDEs with autocomplete make almost irrevelant to read the API documentation, as is intuitive due its self-discoverable capabilities.
edited 57 mins ago
answered 1 hour ago
DanielCuadraDanielCuadra
1914 bronze badges
1914 bronze badges
add a comment
|
add a comment
|
Thanks for contributing an answer to Software Engineering Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fsoftwareengineering.stackexchange.com%2fquestions%2f398828%2fis-there-a-pattern-for-handling-conflicting-function-parameters%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
1
"Recently, a consumer for this API wanted to [provide] the number of months instead of the end date" - This is a frivolous request. They can transform the # of months into a proper end date in a line or two of code on their end.
– Graham
7 hours ago