Einführung
Angenommen, wir haben eine Reihe von Objekten mit einigen Daten, und wir müssen diese Objekte bearbeiten.
Nehmen wir an, das häufigste Beispiel ist das Füllen eines Korbs mit Obst. Wir werden eine bestimmte classart-Klasse implementieren, in die wir Früchte hinzufügen werden. Als nächstes benötigen wir die Basisklasse Fruit, um den Volumenparameter zu definieren, dem wir abhängig von der Frucht einen Wert zuweisen. Das Problem ist gelöst, die Früchte werden hinzugefügt, bis das Volumen des Korbs die Schwelle erreicht.
Nach mehreren Iterationen, wenn unser Programm zum Befüllen von Körben einige faule Birnen, Äpfel mit Würmern gefaltet hat oder etwas anderes schief geht, werden wir die Basis-Fruchtklasse erweitern und ihr neue Parameter für Frische, Reinheit usw. hinzufügen Überprüfungsbedingungen werden angezeigt.
Und hier beginnt das Problem. Ja, unsere Klasse ist vielseitiger geworden, aber nicht nur die Anzahl der Probleme mit Früchten kann zunehmen, sondern auch die Situation, in der Obst selbst gepflückt wird, kann unterschiedlich sein. Zum Beispiel pflücken wir Früchte, bei denen absolut jede Frucht frisch ist. Warum brauchen wir in diesem Fall ein Feld, das für die Frische verantwortlich ist, wenn bekannt ist, dass alle Früchte frisch sind? Zusätzliche Funktionen einzäunen, um obligatorische und optionale Typen anzugeben? - Nein, natürlich. Wir fügen eine Reihe optionaler Typen in ein Array (Wörterbuch) ein, wobei jeder Typ nicht direkt Teil der Klasse ist. Unsere Funktionalität ist wieder schlauer, großartig.
Ich habe mich jedoch entschlossen, dieses Thema ein wenig weiterzuentwickeln.
Idee
Wir definieren einen Typ, der für das Speichern optionaler Variablen im Zeichenfolgenformat verantwortlich ist. Tatsächlich ist es ein Wrapper über einem Array. Als Mindestmenge an Informationen speichern wir den Namen der Variablen, den Typ (es kann entweder basic, int, string, bool oder eine Zusammensetzung aus mehreren grundlegenden sein) und den Wert.
Die Methoden dieses Objekts können variabel sein, aber ich habe Folgendes für mich hervorgehoben:
Abrufen einer Liste von Variablen und ihren Typen im Zeichenfolgenformat (im Allgemeinen können Sie sich auf Namen beschränken). Dies kann nützlich sein, wenn wir ein Objekt mit dem "Ursprung" markieren möchten.
Getter, der unser Array mit Informationen zu Variablen im konstanten Format zurückgibt.
Abrufen der ID des Typs und des Werts anhand des Namens der Variablen (erforderlich für das Umwandeln in einen bestimmten Typ).
class IVariable
{
public:
using Type = std::variant < int, double, bool, std::string>; //
virtual std::vector < std::pair < std::string_view, std::string_view > > abstract() = 0;
virtual std::vector < std::tuple < std::string_view, VarTypes, std::string_view > > released() = 0;
virtual std::pair < VarTypes, std::string_view > released(const std::string_view& name) = 0;
};
, , :
, "" , .
, ""
bool tryVector(const std::string_view& dX)
{
auto type = range.front()->released(dX);
if(type.first == VarTypes::_null)
{
return false;
}
for(auto&& var : _range)
{
if(var->released(dX).first != type.first)
{
return false;
}
}
return true;
}
, ( tryVector).
, , , .
, , .
Die Möglichkeit, den Umfang der Arbeit mit Objekten anzupassen, wenn wir je nach Situation entweder mehr Parameter benötigen oder umgekehrt weniger.
Überlegen Sie nicht, wie Sie ein Objekt entwerfen, sondern welche Aktionen mit dem Objekt ausgeführt werden sollen
Nachteile
Nicht geeignet zur Berechnung einer großen Anzahl von Objekten des gleichen Typs. Für eine Klasse, die die RGB-Komponente eines Pixels definiert, ist es beispielsweise effizienter, die Eigenschaften explizit zu definieren.
Ein solcher Ansatz verbraucht viel Speicher, und dementsprechend ist es sinnvoll, ihn nur für die Arbeit mit Objekten zu verwenden, deren Wesen nicht in Form mehrerer einfacher Variablen ausgedrückt werden kann, d. H. mehr für die logische Verarbeitung von Objekten als für direkte Berechnungen.