My question is about code design in general, but consider the specific context of a numerical integration tool.
Option 1: a class with a state, setters, accessors, and an execute method.
<code>class Integrator
{
public:
// update state: set integration method
void set_method(int method) {_method = method;};
// update state: set integration order
void set_order(int order) {_order = order;};
// accessors to retrieve current state
int method() {return _method;};
int order() {return _order;};
// other settings...
// do integration
double execute();
// return some post-execution info
void get_points_weights(...);
// assume there are safeguards in place in case a user calls a post-execute method before actually executing
private:
// some default state
int _method = 1, _order = 4;
};
</code>
<code>class Integrator
{
public:
// update state: set integration method
void set_method(int method) {_method = method;};
// update state: set integration order
void set_order(int order) {_order = order;};
// accessors to retrieve current state
int method() {return _method;};
int order() {return _order;};
// other settings...
// do integration
double execute();
// return some post-execution info
void get_points_weights(...);
// assume there are safeguards in place in case a user calls a post-execute method before actually executing
private:
// some default state
int _method = 1, _order = 4;
};
</code>
class Integrator
{
public:
// update state: set integration method
void set_method(int method) {_method = method;};
// update state: set integration order
void set_order(int order) {_order = order;};
// accessors to retrieve current state
int method() {return _method;};
int order() {return _order;};
// other settings...
// do integration
double execute();
// return some post-execution info
void get_points_weights(...);
// assume there are safeguards in place in case a user calls a post-execute method before actually executing
private:
// some default state
int _method = 1, _order = 4;
};
Option 2: just a plain old function with some structs to store inputs/outputs.
<code>struct IntegrationSettings
{
method = 1;
order = 4;
// or there could be setters/getters instead
};
struct IntegrationResult
{
// final answer, points, weights, etc.
};
void do_integration(IntegrationResult &result, const IntegrationSettings settings = IntegrationSettings())
{
// do integration based on given settings
}
</code>
<code>struct IntegrationSettings
{
method = 1;
order = 4;
// or there could be setters/getters instead
};
struct IntegrationResult
{
// final answer, points, weights, etc.
};
void do_integration(IntegrationResult &result, const IntegrationSettings settings = IntegrationSettings())
{
// do integration based on given settings
}
</code>
struct IntegrationSettings
{
method = 1;
order = 4;
// or there could be setters/getters instead
};
struct IntegrationResult
{
// final answer, points, weights, etc.
};
void do_integration(IntegrationResult &result, const IntegrationSettings settings = IntegrationSettings())
{
// do integration based on given settings
}
My questions are specific to the context of scientific computing:
- What are some pros and cons of each approach? At first glance, option 2 seems simpler and safer. In option 1, I have to be careful about the state of the class – e.g. “can’t do this before you’ve done that” etc. However, the “memory” in option 1 means I can save some info and reuse it the next time the execute method is called (also possible with option 2 but might be less clean). Even though option 2 seems simpler, I don’t want to shun OOP here before giving it full consideration.
- I want to be able to add support, later, for different integration methods, settings etc. From that perspective, is one option a clear winner? To me it seems roughly the same amount of work in both cases.
- From the user perspective, e.g. ease of setting things up and learning the interface, is one option the clear winner / standard practice? To me, again option 2 seems simpler, but maybe there are things I have not considered?
- Generalizing a bit: I face this choice very often while coding, in different contexts but always coming down to “class vs. function”. What are some guidelines and rules of thumb, especially for scientific computation?