tags:

views:

315

answers:

11

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  A: 

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.

Andrew Rollings
A: 

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.

shahkalpesh
A: 

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.

John MacIntyre
+1  A: 

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.

Charlie Martin
+4  A: 

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.

Jimmy
+1  A: 

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

Andreas Grech
+3  A: 

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.

Bent André Solheim
A: 

It would be helpful to know the application of this Calculator class. How are the outputs consumed? Would it make sense to expose the outputs as properties that could be consumed by listeners based on a calculation completed event. Can you group the inputs into some related classes? Are there fixed or variable numbers of these? Knowing more about the application would really help me to give you an answer.

tvanfosson
+1  A: 

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.

Andrew Kennan
+1  A: 

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

Steven A. Lowe
A: 

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.

Dave Sherohman