vendredi 5 mai 2017

Should I use a class with static members to implement Template Method Pattern if I don't need to take care of internal function state?

The question is a bit confusing so let me clarify by explaining my predicament.

I'm using nlohman::json and trying to do conversions between nlohman::json value types and QStrings from QT 5.8. In order to do those conversions I came to the conclusion that I would need to use the json libraries json::is_typexxx for json value types. This ended up looking like this:

QString convertQString(const json &json_in) {
    if (json_in.is_number_integer()) {
        return //QString integer conversion
    } else if (json_in.is_number_float()) {
        return //QString float conversion
    } else if (json_in.is_string()) {
        return //QString std::string conversion
    } else if (json_in.is_boolean()) {
        return //QString bool conversion
    }
    return // QString no type found default
}

I soon realized that I would need to convert QStrings back into values for nlohman::json, in doing so I started to write the following function

void convertJson(.../**notimportant**/... json& json_out){
    ... /**more stuff here**/
    if (json_out.is_number_integer()) {
        ...
    } else if (json_out.is_number_float()) {
        ...
    } else if (json_out.is_string()) {
        ...
    } else if (json_out.is_boolean()) {
        ...
    }
    ...
}

and soon realized I was just writing the same logic for checking types. Naturally I thought this was an excellent oportunity to use the Template Method Pattern, and started writing the following classes:

template<typename T>
class JsonChoice {
protected:
    T makechoice(const nlohmann::json &json_in);

    virtual T ifinteger() = 0;
    ...// other functions that follow the "iftypexxx()" convention
};

template<typename T>
T JsonChoice::choice(const nlohmann::json &json_in) {
    if (json_in.is_number_integer()) {
        return ifinteger();
    ... //you get the pattern... 
}

QStringConverter : public JsonChoice<QString>{
   ...
protected:
   QString ifinteger();
   ... //etc
};
// friend define operator ()?

JsonConverter : public JsonChoice<nlohmann::json>{
   ...
protected:
   nlohmann::json ifinteger();
   ... //etc
};
// friend define operator ()?

It was at this point that I realized I didn't need state in either of my Template Method Pattern functors. However most examples I've seen of this pattern implemented are made like so:

TMPFunctor = tmpfun(statevar);
...

So I was wary of not following convention here, but since I didn't need any state, it didn't seem like it was a good idea to force myself to have to create a variable every time I wanted to use what was essentially a function with internal logic switched out depending on the signature. I started wondering if I even should use functors to accomplish this task, as I could techinically template every part of the process to simply create one function as well. Is this an adequate solution to this problem, to create a functor class with all static members, use it as a functor?

According to this statically overloading () is a problem however and I'm not sure if I should just wrap another class function in another function like so to solve this issue:

QString convertQstring(const nlohmann::json &json_in){
    return QStringConverter.makechoice(json_in);
}

or just template every part of the function (which sounds annoying and ugly to me) to generalize the choice switch ie:

template<typename returnval, typename integerfunctemplate, ...>
returnval convert( const nlohmann::json &json_in){
    if (json_in.is_number_integer()) {
        return integerfunctemplate();
    ...
}

Aucun commentaire:

Enregistrer un commentaire