[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() ) nicht typeid 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() ) nicht typeid 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.


Anmelden zum Antworten