问题
I need to perform a complicated calculation. In my case it seemed most natural to create a Calculator class (abstracted using a strategy pattern).
To perform the calculation the class needs to accept about 20 inputs, some of which are optional, some of which could change in the future etc. Once the Calculate() method is called, about 20 different variables need to be outputted.
There are a number of ways this can be achieved.
- Inputs passed in as parameters to a calculate method
- Inputs passed in through properties of the Calculator
- Inputs wrapped up into their own class, then passed to Calculate() method.
- Outputs returned by Calculate(), wrapped up in a class
- Outputs populated into a parameter passed to the Calculate() method
- Outputs retrieved from public properties of the Calculator, after Calculate() has been called
There are pros and cons to all these methods. How would you do it?
UPDATE: Thanks for the feedback.
The purpose of this calculator is to generate a quote. The inputs are things such as the customer's address, interest rates, target profit, additional fees, product id etc The output includes the quote, the actual profit, more fees etc.
I have gone ahead and created ICalculateInput and ICalculateOutput interfaces and their concrete classes, and the system works very well now. The Calculator class also inherits from an ICalculator interface (as the calculations involved differ enormously depending on the company the product is sourced from).
回答1:
Most people suggest using a "Parameter Class", and a "Result Class". I agree with this approach, but it seems to me that your parameters fall into several categories. Perhaps you could create a parameter class for the required parameters, and a separate parameter class for the optional parameters or groups of optional parameters. This way, you could create different methods depending on what kind of calculation you require;
Result calculate(RequiredArgs requiredArgs) {
...
}
Result calculate(RequiredArgs requiredArgs, OptionalArgs optionalArgs) {
}
Result calculate(RequiredArgs requiredArgs, OptionalArgs optionalArgs, OtherOptionalArgs oOpitonalArgs) {
}
This will make your API easier to use. If you don't want to create classes for the different groups, you could also use Maps, but this will require more validation code in the calculation engine. Normally I would prefer parameter classes, but would have to know more about your particular problem to be decisive.
I would not store the calculation result in the calculation engine itself, because of thread safety and object reusability. It's much easier to maintain code that is stateless.
回答2:
I'd recommend
- Inputs wrapped up into their own class, then passed to Calculate() method.
- Outputs returned by Calculate(), wrapped up in a class
storing state in the Calculator only makes sense if you have a multi-step calculation, or if the fundamental calculation step might be done more than once and re-populating the inputs is hard. Otherwise, it's a bad abstraction that's going to to fail when you multithread it or reuse the same object in different parts of the code.
having lots of parameters is hard to maintain and read, and is inflexible if you need to change things
mutating parameters to generate output is a bad idea. its not obvious from the callers side that a class he 'owns' has been changed by passing it into a function.
回答3:
Long parameter lists get onerous and error-prone. Tell us a little more about the application and environment, but in general, it would be tempting to either pass an object of some class or use a list or the like.
One fairly nifty way to deal with it that I've done is to use the Composite pattern. In Java, you might, for example, make an interface of Parameter and then make a list of objects implementing Parameter.
回答4:
As a rule of thumb, do not create methods which accept more than 3-4 individual parameters
Something you shouldn't do in JavaScript:
var addUser = function (name,surname, age, add1, add2, telephone) {
//do something
};
Instead of the above, its better do something like such:
var addUser = function (userDetails) {
//Do something with userDetails.name etc...
};
//Then invoke the function by passing in an object:
var ud = {name : 'Andreas', surname : 'Grech', age : 20, add1 : 'bla', add2 : 'bla', telephone : 12343};
addUser(ud);
That way you can invoke the function without breaking it by entering the parameters in any order you like, and you can even skip some
回答5:
I agree that your inputs and outputs should be contained in their own classes.
One possibility you might want to consider is using the Builder pattern to construct the input object if some or all of the parameters are optional.
回答6:
you left out
- inputs placed into a Dictionary, which is passed to Calculate
one wonders why you have 20 inputs to a single function... seems excessive. But if you need them, and some are optional, and the calculation method may be altered in future via a Strategy pattern, then passing in a collection of named variables might make sense. You could even specify the Strategy pattern in the collection as well, so that the Calculate method is completely generic
回答7:
You haven't mentioned the language, but I'll assume c#. I would probably pass them in as a struct (or class) and return the outputs the same way.
Alternatively, I'd find some way to refactor it and simplify the expected input/output.
回答8:
Personally, I would create a custom input and output struct or class, and prepopulate those, pass them in, and recieve a return value of the output struct or class.
回答9:
Try doing what database provider classes in .net do.
Have a parameter class (with a type and value property with direction i.e input/output) & have a parameters (collection of parameter) as property of the Calculator.
For details, have a look at OleDBCommand/SQLCommand class in .net which is used to call stored procedures/functions.
回答10:
Assuming that you've already thought hard about this and determined that, yes, you really do need all those parameters:
I would most likely use named parameters, but my language of choice (Perl) supports order-independent named parameters. If yours does not, then passing in an object is the next best choice. Having to pass more than 2-3 parameters in a specific order (named or not) is just asking for trouble.
For the output, I'd most likely return an object if there's more than one value coming back.
来源:https://stackoverflow.com/questions/351165/dealing-with-lots-of-input-parameters-lots-of-outputs