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
Did Tolkien ever write about a Heaven or Hell for Men?
How does Monks' Improved Unarmored Movement work out of combat?
Incomplete iffalse: How to shift a scope in polar coordinate?
How to study endgames?
Can you cure a Gorgon's Petrifying Breath before it finishes turning a target to stone?
Calculate the Ultraradical
Delete n lines skip 1 line script
grounded outlets and code compliance
What are one's options when facing religious discrimination at the airport?
Beyond Futuristic Technology for an Alien Warship?
Why does `FindFit` fail so badly in this simple case?
Replacing cord for IBM model M keyboard
How do I introduce dark themes?
Is there an in-universe explanation of how Frodo's arrival in Valinor was recorded in the Red Book?
Windows 10 deletes lots of tiny files super slowly. Anything that can be done to speed it up?
Verb ending in -ん with positive meaning?
Why has Speaker Pelosi been so hesitant to impeach President Trump?
How can I visualize an ordinal variable predicting a continuous outcome?
If a spaceship ran out of fuel somewhere in space between Earth and Mars, does it slowly drift off to the Sun?
If someone asks a question using “quién”, how can one shortly respond?
Accessing JSON fields in html of LWC
Create the same subfolders in another folder
Would an object shot from earth fall into the sun?
Do my potential customers need to understand the "meaning" of a logo, or just recognize it?
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
4
"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
9 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 9 hours ago
CalMlynarczyk
asked 10 hours ago
CalMlynarczykCalMlynarczyk
1244 bronze badges
1244 bronze badges
4
"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
9 hours ago
add a comment
|
4
"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
9 hours ago
4
4
"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
9 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
9 hours ago
add a comment
|
3 Answers
3
active
oldest
votes
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 necessarily a "design pattern".
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
9 hours ago
2
@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
8 hours ago
1
Using a type is not wrong, but it actually does not really solve the OPs problem. In the current case it does only shift the problem from the existing function to the constructor of the type (or a factory method). In JavaScript, you need different function names to distinguish between semantically different functions with similar number of arguments, that would be necessary for the factory methods for the new type as well. So no, this is not just an implementation detail - introducing a type is here just a red herring (but my congratulations, at least 5 upvoters took the bait).
– Doc Brown
1 hour ago
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
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 necessarily 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 necessarily 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 necessarily 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 necessarily a "design pattern".
edited 10 mins ago
answered 9 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
|
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
9 hours ago
2
@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
8 hours ago
1
Using a type is not wrong, but it actually does not really solve the OPs problem. In the current case it does only shift the problem from the existing function to the constructor of the type (or a factory method). In JavaScript, you need different function names to distinguish between semantically different functions with similar number of arguments, that would be necessary for the factory methods for the new type as well. So no, this is not just an implementation detail - introducing a type is here just a red herring (but my congratulations, at least 5 upvoters took the bait).
– Doc Brown
1 hour 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
9 hours ago
2
@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
8 hours ago
1
Using a type is not wrong, but it actually does not really solve the OPs problem. In the current case it does only shift the problem from the existing function to the constructor of the type (or a factory method). In JavaScript, you need different function names to distinguish between semantically different functions with similar number of arguments, that would be necessary for the factory methods for the new type as well. So no, this is not just an implementation detail - introducing a type is here just a red herring (but my congratulations, at least 5 upvoters took the bait).
– Doc Brown
1 hour 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 9 hours ago
AlexanderAlexander
1,3378 silver badges16 bronze badges
1,3378 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
9 hours ago
2
@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
8 hours ago
1
Using a type is not wrong, but it actually does not really solve the OPs problem. In the current case it does only shift the problem from the existing function to the constructor of the type (or a factory method). In JavaScript, you need different function names to distinguish between semantically different functions with similar number of arguments, that would be necessary for the factory methods for the new type as well. So no, this is not just an implementation detail - introducing a type is here just a red herring (but my congratulations, at least 5 upvoters took the bait).
– Doc Brown
1 hour 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
9 hours ago
2
@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
8 hours ago
1
Using a type is not wrong, but it actually does not really solve the OPs problem. In the current case it does only shift the problem from the existing function to the constructor of the type (or a factory method). In JavaScript, you need different function names to distinguish between semantically different functions with similar number of arguments, that would be necessary for the factory methods for the new type as well. So no, this is not just an implementation detail - introducing a type is here just a red herring (but my congratulations, at least 5 upvoters took the bait).
– Doc Brown
1 hour 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
9 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
9 hours ago
2
2
@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
8 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
8 hours ago
1
1
Using a type is not wrong, but it actually does not really solve the OPs problem. In the current case it does only shift the problem from the existing function to the constructor of the type (or a factory method). In JavaScript, you need different function names to distinguish between semantically different functions with similar number of arguments, that would be necessary for the factory methods for the new type as well. So no, this is not just an implementation detail - introducing a type is here just a red herring (but my congratulations, at least 5 upvoters took the bait).
– Doc Brown
1 hour ago
Using a type is not wrong, but it actually does not really solve the OPs problem. In the current case it does only shift the problem from the existing function to the constructor of the type (or a factory method). In JavaScript, you need different function names to distinguish between semantically different functions with similar number of arguments, that would be necessary for the factory methods for the new type as well. So no, this is not just an implementation detail - introducing a type is here just a red herring (but my congratulations, at least 5 upvoters took the bait).
– Doc Brown
1 hour ago
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 2 hours ago
answered 2 hours 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
4
"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
9 hours ago