19

I have a reasonably-sized class that implements several logically-related algorithms (from graph theory). About 10-15 parameters are required as input to the algorithm. These are not modified by the algorithm, but are used to guide the operation of it. First, I explain two options for implementing this. My question is what is a common way to do so (whether it is or isn't one of the two options).

I personally don't like to pass these values as parameters to the function when N is large, especially while I'm still developing the algorithm.

void runAlgorithm(int param1, double param2, ..., bool paramN);

Instead I have a class Algorithm that contains the algorithms, and I have a struct AlgorithmGlobals that contains these parameters. I either pass this struct to:

void runAlgorithm(AlgorithmGlobals const & globals);

Or I add a public AlgorithmGlobals instance to the class:

class Algorithm {
public:
    AlgorithmGlobals globals;
    void runAlgorithm();
}

Then elsewhere I'd use it like this:

int main() {
    Algorithm algorithm;
    algorithm.globals.param1 = 5;
    algorithm.globals.param2 = 7.3;
    ...
    algorithm.globals.paramN = 5;

    algorithm.runAlgorithm();

    return 0;
}

Note that the constructor of AlgorithmGlobals defines good defaults for each of the parameters so only the parameters with non-default values need to be specified.

AlgorithmGlobals are not made private, because they can be freely modified before the runAlgorithm() function is called. There is no need to "protect" them.

Alan Turing
  • 12,223
  • 16
  • 74
  • 116

6 Answers6

12

This is called the "Parameter object" pattern, and it's generally a good thing. I don't like the member version, especially calling it "XGlobals" and implying that it's shared all over the place. The Parameter Object pattern instead generally involves creating an instance of the Parameter Object and passing it as a parameter to a function call.

Brent Matzelle
  • 4,073
  • 3
  • 28
  • 27
Ernest Friedman-Hill
  • 80,601
  • 10
  • 150
  • 186
9

Others have mentioned Parameter Object, but there is also another possibility: using a Builder.

Builder allows you to omit the parameters whose default values are suitable, thus simplifying your code. This is especially handy if you are going to use your algorithm with several different sets of parameters. OTOH it also allows you to reuse similar sets of parameters (although there is a risk of inadvertent reuse). This (together with method chaining) would allow you to write code such as

Algorithm.Builder builder;

Algorithm a1 = builder.withParam1(1).withParam3(18).withParam8(999).build();
...
Algorithm a2 = builder.withParam2(7).withParam5(298).withParam7(6).build();
Péter Török
  • 114,404
  • 31
  • 268
  • 329
6

You have several different ideas that you should be suggesting with your design:

  1. The parameters are purely inputs.
  2. The parameters are specific to your algorithm.
  3. The paramaters have default values that are sane.

class Algorithm {
  public:
    class Parameters { // Nested class, these are specific to your algorithm.
      public:
        Parameters() : values(sensible_default) { }
        type_t values; // This is all about the data.
    };

    Algorithm(const Parameters &params) : params_(params) { }

    void run();

  private:
    const Parameters params_; // Paramaeters don't change while algorithm
};                            // is running. 

This is what I would suggest.

Omnifarious
  • 54,333
  • 19
  • 131
  • 194
  • Great answer. I'm a little new to StackOverflow, and am now sitting, looking at the several great answers and wondering which one to pick as the answer, lol. This one is so clear and well described! ;-) – Alan Turing Apr 05 '11 at 12:45
  • @Lex Fridman - I would pass the parameters to a function call, except that you called your class 'Algorithm', which implies that it's basically an encapsulation of a run of your algorithm. Also, I had to edit as I realized that the declaration of `params_` was illegal unless the definition of `Parameters` was inside the class. And thanks! :-) – Omnifarious Apr 05 '11 at 12:47
  • STL uses this approach a lot in its implementation. – Amir May 06 '19 at 11:29
4

I use this technique that you already mentioned:

void runAlgorithm(AlgorithmGlobals const & globals);

But would call the class AlgorithmParams instead.

Chris O
  • 5,017
  • 3
  • 35
  • 42
4

The Named Parameter Idiom might be useful here.

a.runAlgorithm() = Parameters().directed(true).weight(17).frequency(123.45);
Robᵩ
  • 163,533
  • 20
  • 239
  • 308
1

suggestion Why don't you do this instead:

class Algorithm {
public:
Algorithm::Algorithm(AlgorithmGlobals const & globals) : globals_(globals) {}

    void runAlgorithm(); // use globals_ inside this function

   private:
    const AlgorithmGlobals globals_;
    };

Now you can use it as such:

AlgorithmGlobals myglobals;
myglobals.somevar = 12;

Algorithm algo(myglobals);
Tony The Lion
  • 61,704
  • 67
  • 242
  • 415
  • 1
    This is my favorite solution. Except, since the OP says the parameters remain constant while the algorithm is running, I would make it `const AlgorithmGlobals _globals;`. Also, leading underscores are reserved in the C++ standard. Use a trailing underscore (i.e. `globals_` not `_globals`). – Omnifarious Apr 05 '11 at 12:27
  • 1
    `_globals` is not reserved in this context. – Steve Jessop Apr 05 '11 at 12:31