Keep the configuration out of the code
Posted on Oct 27, 2021, three minutes to read.Sometimes, it’s not a complex design pattern but a small idea you borrow from a different language that will get the job done.
On one project, I had a factory method where an appropriate class is used based on a configuration:
class Style {
public:
void apply(Application *app);
static void install(Application *app, const Config &config);
protected:
//methods like
virtual std::map<std::string, std::string> getMap() const = 0;
virtual std::vector<std::string> getList() const = 0;
}
void Style::install(Application *app, const Config &config) {
if (config.setting1 == 'a') {
Style1().apply(app);
} else if (config,setting1 == 'b') {
Style2().apply(app);
}
}
void Style::apply(Application *app) {
//called on subclasses of the Style class, calls overloaded methods from the implementation
//getMap();
//getList();
//apply the style on app
}
Over time this block of code grew substantially. The amount of possible values for setting1 skyrocketed. Also, other settings came into play.
My initial idea was to use a map of possible Style classes, pick one based on a setting1
value and go from there as I would in Python:
map = {
'a': Style1,
'b': Style2
}
map[setting1]().apply(app)
This approach turned out tricky to pull off in C++. Nevertheless, it brought me an idea. After an investigation, I found out the Style classes are internally effectively JSONs - that is, they only contain a bunch of properties we read one way or another. Instead of selecting an appropriate class, I can pick a JSON file, load the values from the JSON instead of a Style class and process that the way I do now.
With that in mind, it was sufficient to replace all the Style classes with a JSON object. I store the JSON in resources for all configurations. It is then enough to extract maps and lists from the JSON and directly process those. As a result, we no longer need the inheritance nor mangroves of conditions.
class Style {
public:
static void install(Application *app, const Config &config);
}
void Style::install(Application *app, const Config &config) {
//load maps and lists from the JSON
//use the code from original apply() to process those maps and lists
//apply the style on the app
}