Voici un exemple simplifié de ce qu'on appelle (j'espère - corrigez-moi si je me trompe) Stratégie modèle: il y a une classe FileWriter
qui écrit des paires valeur-clé dans un fichier et utilise l'objet de l'interface IFormatter
pour le formatage du texte en cours d'écriture. Il existe différentes implémentations de formateurs et l'objet formateur est passé lorsque FileWriter
est créé. est ici une (mauvaise) mise en œuvre de ce modèle:Modèle de stratégie en C++. Options d'implémentation
#include <iostream>
#include <fstream>
#include <stdlib.h>
#include <sstream>
using namespace std;
class IFormatter {
public:
virtual string format(string key, double value) = 0;
};
class JsonFormatter : public IFormatter {
public:
string format(string key, double value) {
stringstream ss;
ss << "\""+key+"\": " << value;
return ss.str();
}
};
class TabFormatter : public IFormatter {
public:
string format(string key, double value) {
stringstream ss;
ss << key+"\t" << value;
return ss.str();
}
};
class FileWriter {
public:
FileWriter(string fname, IFormatter& fmt):fmt_(fmt)
{
f_.open(fname.c_str(), ofstream::out);
}
void writePair(string key, double value)
{
f_ << fmt_.format(key, value);
}
private:
ofstream f_;
IFormatter& fmt_;
};
Comme on le voit, l'inconvénient principal de cette approche est qu'il est manque de fiabilité-Formatter
objet passé à FileWriter
doit exister au cours de la vie de toute FileWriter
, appelle donc comme FileWriter("test.txt", JsonFormatter())
conduire directement à SegFault
.
À cet égard, je voudrais discuter ce qui pourrait être les autres options pour la mise en œuvre d'une telle approche avec « facile à utiliser » et les exigences de simplicité:
- soit nouvelle formatter peut être transmis lorsque l'auteur du fichier est créé, ou
- le formateur existant peut être transmis et utilisé.
je suis venu avec plusieurs variantes décrites ci-dessous avec leurs inconvénients (OMI):
- modèles: ayant
FileWriter
en tant que classe de modèle qui prendFormatterClass
exactement comme argument; inconvénient: moche d'appeler:FileWriter<JsonFormatter>("test.txt", JsonFormatter())
- ici,JsonFormatter
est tapé deux fois. - pointeurs bruts:
FileWriter("test.txt", new JsonFormatter())
; drawback - qui doit supprimer l'objet formateur?FileWriter
? si oui, le fait de passer une adresse du formateur existant conduira àSegFault
une fois que l'objetFileWriter
aura tenté de supprimer le formateur. - pointeurs partagés:
FileWriter("test.txt", dynamic_pointer_cast<IFormatter*>(shared_ptr<JsonFormatter*>(new JsonFormatter()))
; inconvénient: moche à appeler, et encore, et si formatter a été créé avant la création de l'écrivain de fichier?
Quelles seraient les meilleures pratiques ici?
MISE À JOUR
En réponse aux réponses qui suggéraient d'utiliser std::function
- Que faire si Formatter peut stocker un état (par exemple, la précision) et ont d'autres méthodes, comme getHeader()
, par exemple, pour les fichiers CSV?
De plus, stocker IFormatter
par valeur n'est pas possible car il s'agit d'une classe abstraite.
alors que cette solution fonctionnera, en effet, la conception décrite en question me semble plutôt être une approche générale qui devrait être supportée par des capacités de bibliothèque de langage/standard (ou nécessiter un design/modèle différent) manipulez-le en écrivant des méthodes 'clone()' pour ses classes. – peetonn