Keep the configuration out of the code
Sometimes it’s not a design pattern but a small idea borrowed from another language that gets the job done.
The setup #
A factory method picked a Style subclass based on the configuration:
class Style {
public:
void apply(Application *app);
static void install(Application *app, const Config &config);
protected:
// implemented by each subclass
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) {
// reads getMap() and getList() from the subclass
// and applies the style to app
}
Over time, this block grew substantially. The number of possible values for setting1 skyrocketed, and other settings came into play.
The idea from Python #
In Python, I’d map each value to a class and instantiate the one I need:
styles = {
'a': Style1,
'b': Style2,
}
styles[setting1]().apply(app)
This turned out tricky to pull off in C++. It did make me look at what the Style classes actually held: each one only returned a fixed set of properties. In effect, each class was a JSON document.
The fix: one JSON file per configuration #
Instead of selecting a class, the code can select a JSON file and read the same values from it. I replaced all the Style subclasses with JSON files stored in the resources, one per configuration. install extracts the maps and lists from the JSON and processes them with the code from the original apply. Neither the inheritance nor the chain of conditions is needed anymore:
class Style {
public:
static void install(Application *app, const Config &config);
};
void Style::install(Application *app, const Config &config) {
// load the maps and lists from the JSON for this configuration
// process them with the code from the original apply()
// apply the style to app
}
When subclasses differ only in the values they return, they are configuration, not code.