[verworfen] Frage zur (möglichkeit von) Templatisierung
-
Ich bin unsicher ob und wie ich hier templatisieren sollte (habe noch nie mit Templates gearbeitet):
Ich habe eine Basisklasse die die Funktionalität bereitstellt: BasicDbItem und einige abgeleiteten Klassen (DBroot, DataBase2, Experiment2, Subject2, Trial2, ModalitBase2(pure virtual), Mod1, Mod2, Mod3, Mod4 (jedes ModX ist eine Implementation der ModalitBase2). Bis auf die ModalityBase2 und die ModX werden im Grunde genommen immer nur einige Wrappermethoden mit eingen Namen bereitstellen (die nur BasicDbItem-Methoden nutzen) sowie einige dynamic_casts gemacht. Macht es da Sinn irgendwo zu templatisieren?
Als simplifiziertes Beispiel:
basicdbitem.h [original 75 Zeilen]
#ifndef BASICDBITEM_H #define BASICDBITEM_H #include <QtCore> enum BasicDbItemType {BasicDbItem_root=0, BasicDbItem_database, BasicDbItem_experiment, BasicDbItem_subject, BasicDbItem_trial, BasicDbItem_modality, BasicDbItem_other, BasicDbItem_size}; class BasicDbItem { public: explicit BasicDbItem(const BasicDbItemType &, const QString &); virtual ~BasicDbItem(); // metainfo getter virtual const BasicDbItemType getType() const { return mType; } virtual const QString getName() const { return mName; } // child adder virtual bool appendChild(const BasicDbItem *); // child getter virtual const BasicDbItem * getChildByName(const QString &) const; protected: BasicDbItemType mType; QString mName; QList< const BasicDbItem * > mChilds; }; #endif // BASICDBITEM_H
basicdbitem.cpp [original 550 Zeilen]
#include "basicdbitem.h" // constructor BasicDbItem::BasicDbItem(const BasicDbItemType &pType, const QString &pName) : mType(pType), mName(pName) { QString einrueck(QString(mType*4,' ')); QString text = einrueck.append(QString("creating: Type(%1) Name (%2)").arg((int)mType).arg(mName)); qDebug() << text; } // destructor BasicDbItem::~BasicDbItem() { QString einrueck(QString(mType*4,' ')); QString text = einrueck.append(QString("deleting: Type(%1) Name (%2)").arg((int)mType).arg(mName)); qDebug() << text; qDeleteAll(mChilds); mChilds.clear(); } // child adder bool BasicDbItem::appendChild(const BasicDbItem * pChild) { if (pChild == 0) // null-pointer) { return false; } // only allow direct childs (DB->EXP, EXP->SUBJ, ...) if (this->getType() == (pChild->getType()-1) ) { mChilds.append(pChild); return true; } return false; } // child getter const BasicDbItem * BasicDbItem::getChildByName (const QString &pName) const { foreach (const BasicDbItem * item, mChilds) { if (item->getName() == pName) { return item; } } return 0; }
Dann gibt es immer gleich aufgebaute, abgeleitete Klassen (für Database, Experiment, Subject, Trial, Modality):
database2.h
#ifndef DATABASE2_H #define DATABASE2_H #include <QtCore/QString> #include "basicdbitem.h" class Experiment2; class DataBase2 : public BasicDbItem { public: DataBase2(const QString &pName) : BasicDbItem(BasicDbItem_database, pName) {}; const void addExperiment(const BasicDbItem *); const Experiment2 * getExperimentByName(const QString &); }; #endif // DATABASE2_H
database2.cpp
#include "database2.h" #include "experiment2.h" const void DataBase2::addExperiment(const BasicDbItem * pExperiment) { BasicDbItem::appendChild(pExperiment); } const Experiment2 * DataBase2::getExperimentByName(const QString &pName) { // retreive child const BasicDbItem * child = BasicDbItem::getChildByName(pName); // cast to correct type return dynamic_cast<const Experiment2 *>(child); }
----------------------------------------------------------------------
experiment2.h
#ifndef EXPERIMENT2_H #define EXPERIMENT2_H #include <QtCore/QString> #include "basicdbitem.h" class Subject2; class Experiment2 : public BasicDbItem { public: Experiment2(const QString &pName, const int &id) : BasicDbItem(BasicDbItem_experiment, pName, id) {}; const void addSubject(const Subject2 *); const Subject2 * getSubjectByName(const QString &); }; #endif // EXPERIMENT2_H
experiment2.cpp
#include "experiment2.h" #include "subject2.h" const void Experiment2::addSubject(const Subject2 * pSubject) { BasicDbItem::appendChild(pSubject); } const Subject2 * Experiment2::getSubjectByName(const QString &pName) { // retreive child const BasicDbItem * child = BasicDbItem::getChildByName(pName); // cast to correct type return dynamic_cast<const Subject2 *>(child); }
[usw...]
----------------------------------------------------------------------
Eigentlich "merkt" sich die BasicDbItem nur den Typ und eine eindeutige ID (nicht Typ und Name) des Datensatzes den sie repräsentiert, alles andere wird zur Laufzeit aus einer MySQL-Db gezogen.
Tabellenstruktur exemplarisch:+-----------------------------------------+ | TABLE: DB (root table, differs a bit) | +---------+------------------+------+-----+---------+ | Field | Type | Null | Key | Default | +---------+------------------+------+-----+---------+ +---> | idDB | int(10) unsigned | NO | PRI | NULL | | | name | varchar(45) | NO | | NULL | | | type | int(10) unsigned | NO | | NULL | | | ... mehr Daten Felder ... | | +---------+------------------+------+-----+---------+ | | +----------------------------+ | | TABLE: EXP | | +---------+------------------+------+-----+---------+ | | Field | Type | Null | Key | Default | | +---------+------------------+------+-----+---------+ | | idEXP | int(10) unsigned | NO | PRI | NULL | <---+ +--- | fk_idDB | int(10) unsigned | NO | MUL | NULL | | | name | varchar(45) | NO | | NULL | | | type | int(10) unsigned | NO | | NULL | | | ... mehr Daten Felder ... | | +---------+------------------+------+-----+---------+ | | +----------------------------+ | | TABLE: SUB | | +---------+------------------+------+-----+---------+ | | Field | Type | Null | Key | Default | | +---------+------------------+------+-----+---------+ | +---> | idSUB | int(10) unsigned | NO | PRI | NULL | | | | fk_idSUB| int(10) unsigned | NO | MUL | NULL | ----+ | | name | varchar(45) | NO | | NULL | | | type | int(10) unsigned | NO | | NULL | | | ... mehr Daten Felder ... | | +---------+------------------+------+-----+---------+ | | [usw]
-
Was soll denn da via Templates realisiert werden?
-
Könntest du vielleicht zwei verschiedene abgeleitete Klassen zeigen, damit man die Gemeinsamkeiten besser erkennt?
Übrigens:
enum BasicDbItemType {BasicDbItem_root=0, BasicDbItem_database, BasicDbItem_experiment, BasicDbItem_subject, BasicDbItem_trial, BasicDbItem_modality, BasicDbItem_other, BasicDbItem_size};
Das würde ich eher mit dem Namensraum-Enum-Idiom lösen:
namespace BasicDbItem { enum Type { root, // erster Wert ist immer 0 database, experiment, subject, trial, modality, other, size, }; } BasicDbItem::Type type = BasicDbItem::experiment;
Aber willst du hierfür (und damit auch für
getType()
) nichttypeid
benutzen? Dann müsste man nämlich nicht schon bei der Basisklasse wissen, welche abgeleiteten Klassen es geben wird und könnte die Erweiterbarkeit einfacher machen.
-
Nexus schrieb:
Könntest du vielleicht zwei verschiedene abgeleitete Klassen zeigen, damit man die Gemeinsamkeiten besser erkennt?
Habe ich oben reineditiert (Implementation der Experiment2.cpp)
Nexus schrieb:
Das würde ich eher mit dem Namensraum-Enum-Idiom lösen:
Das sieht schöner aus, werde ich machen.
Nexus schrieb:
Aber willst du hierfür (und damit auch für
getType()
) nichttypeid
benutzen? Dann müsste man nämlich nicht schon bei der Basisklasse wissen, welche abgeleiteten Klassen es geben wird und könnte die Erweiterbarkeit einfacher machen.Da das ganze ein Wrapper für eine existierende MySQL-Struktur wird stehen die Klassen (== Tabellen) bereits fest. Würde das dann gegen eine Templatisierung sprechen, wenn es nur ~5 Hauptklassen (Databases, Experiments, Subjects, Trials, ModalityBase[virtual]) und dann nochmal so 10 verschiedene Implementierungen von ModalityBase gibt die sich nur in "nicht templatisierbarem" (== "Modalitätsspezifischen Methoden") voneinander unterscheiden?
Edit: habe nochmal ein bisschen nach templates gegoogled und denke nun es wäre unsinnig das hier zu versuchen. Ich müsste dann mit Templatespezialisierung arbeiten und dann wird das ganze umfangreicher als die einfachen abgeleiteten Wrapperklassen.