Fragen zu Klassendesign



  • Ich bin dabei neue Klassen zu planen. Dabei bin ich über folgendes Problem gestolpert:

    Ich habe eine Klasse "Basis", die Funktionen und Werte enthält die die Klassen A,B,C,D benötigen.

    Die Klasse "Simulation" erbt je nach benötigten Komponenten von A,B,C,D oder nur einem Teil davon.

    Jetzt möchte ich innerhalb von Simulation z.B. die Schrittweite dT einstellen, die Bestandteil von Basis ist. A,B,C,D als auch Simulation sollen darauf zugreifen können.

    Wie implementiere ich sowas. Ich möchte das A,B,C,D immer auf dieselbe Klasseninstanz Basis zurückgreifen, sonst müsste ich den Wert ja in A,B usw einzeln einstellen.

    Ich habe derzeit kein Buch zur Hand (die habe ich nur auf der Arbeit) in dem ich ein Beispiel dafür nachschlagen könnte.

    Matthias



  • Wenn die Schrittweite global für alle Instanzen gleich sein soll, dann erstell einfach eine statische Variable in der Basisklasse.

    Wenn du jedoch je nach Simulationsinstanz verschiedene Schrittweiten haben willst, dann wird es etwas schwieriger.

    Außerdem klingt dein bisheriges Design danach, als ob du mehrere Instanzen der Basisklasse hast (nämlich je A, B, C und D).

    Als Abhilfe müßtest du dann virtuelle Vererbung einsetzen, jedoch gibt es dabei den "Diamond of Death" zu beachten. Wenn dir der Begriff nichts sagt, dann such mal danach... (z.B. http://www.gotw.ca/gotw/037.htm)



  • Virtuelle Vererbung war das was ich gesucht habe. In dem Text von dir wird folgendes ausgesagt:

    Finally, the main complication of virtual base classes is that they must be initialized directly by the most-derived class.

    kannst du kurz beschreiben was das bedeutet? Muss ich in 'Simulation' jeweils den Konstruktor von A,B, usw explizit aufrufen?

    Matthias



  • Das ganze Design sieht für mich ziemlich verquer aus. Kannst du mal beschreiben was diese Simulation macht?



  • ehrlich schrieb:

    Das ganze Design sieht für mich ziemlich verquer aus. Kannst du mal beschreiben was diese Simulation macht?

    Laser Dynamics simulieren. Aber das ist für die Fragestellung vollkommen unerheblich. Natürlich könnte ich auch alles in eine einzige Datei packen. Aber ich möchte das ganze erst abstrahieren und danach zu einer Klasse mit nur den benötigten Elementen wieder zusammenfassen.



  • pospiech schrieb:

    ehrlich schrieb:

    Das ganze Design sieht für mich ziemlich verquer aus. Kannst du mal beschreiben was diese Simulation macht?

    Laser Dynamics simulieren. Aber das ist für die Fragestellung vollkommen unerheblich.

    Ja, so allgemein schon. Ich hätte eigentlich wissen wollen was die A B C D Klassen machen. Wenns um verschiedenen Aktionen geht, könntest du dir mal das Command Pattern anschauen.



  • A B usw wären in dem Fall sowas wie 'Kristall', 'Pumpe', 'Resonator' usw. Alles Komponenten die nicht direkt miteinander zu tun haben. Und in 'Basis' sind alle Variablen und Funktionen enthalten die alle Klassen benötigen, z.B. die Zeitschrittweite der Simulation.

    Alles was ich zu Command Pattern finden konnte sah mir nach simpler Multiple Inherence aus (z.B. http://en.wikipedia.org/wiki/Command_pattern). Aber das geht an meinem Problem ja vorbei.



  • Und warum ist eine Simulation ein Kristall, Pumpe oder Resonator? Warum nicht einfach eine Liste von allen benötigten Komponenten halten? Was machst du denn, wenn du für eine Simulation 2 Pumpen brauchst? 2 mal erben?



  • ehrlich schrieb:

    Und warum ist eine Simulation ein Kristall, Pumpe oder Resonator? Warum nicht einfach eine Liste von allen benötigten Komponenten halten? Was machst du denn, wenn du für eine Simulation 2 Pumpen brauchst? 2 mal erben?

    Stimmt das ist ein Fehler im Design.
    Ich könnte genauso auch die Elemente statt zu erben als Objekte in der Klasse haben. Trotzdem müssten alle die Daten aus 'Basis' kennen. Die virtuelle Vererbung würde ich dann beibehalten. Nur die multiple Vererbung nach Simulation würde wegfallen.

    EDIT:
    geht das überhaupt?

    class ModeLockedLaser : public QThread, virtual public LaserdynamicBasics
    {
    ...
    protected:
    	Absorber absorber;
    	Lasermedium lasermedium;
    	PumpLaser pump;
    	Resonator resonator;
    ...
    
    ModeLockedLaser::ModeLockedLaser( QObject* parent /*= 0*/ ) 
    	: QThread(parent), LaserdynamicBasics()
    {
    
    }
    
    class Absorber : virtual public LaserdynamicBasics
    {
    
    class LaserdynamicBasics 
    {
    
    public:
    	LaserdynamicBasics();
    	virtual ~LaserdynamicBasics();
    
    protected:
    	double m_T; // Time 
    	double m_dt; // Time Resolution
    	int N;		// Points of Caluclation (Size of Array)
    ...
    

    Die Variablen
    ModeLockedLaser::absorber.N
    ModeLockedLaser::N

    sollen dabei identisch sein. Funktioniert das damit?



  • Du musst das nicht mit mehrfach und virtueller Vererbung machen. Wenn du Dinge gruppieren willst kannst du das z.B. auch mit dem Composite Pattern oder ähnlichem machen.



  • Du deckst deinen Designfehler doch bereits selbst auf! Du schreibst die Komponenten sollen die "Basis" kennen. Das ist definitiv keine Ist-Ein- oder Verhält-Sich-Wie-Beziehung. Eine Kennt-Beziehung implmentiert man mit einem Verweis auf das entsprechende Objekt (Zeiger, falls es variabel sein soll; Referenz, wenn eine Komponente während der gesamten Laufzeit mit einer "Basis" verbunden ist.).

    Gruß
    Don06



  • Don06 schrieb:

    Du deckst deinen Designfehler doch bereits selbst auf! Du schreibst die Komponenten sollen die "Basis" kennen. Das ist definitiv keine Ist-Ein- oder Verhält-Sich-Wie-Beziehung. Eine Kennt-Beziehung implmentiert man mit einem Verweis auf das entsprechende Objekt (Zeiger, falls es variabel sein soll; Referenz, wenn eine Komponente während der gesamten Laufzeit mit einer "Basis" verbunden ist.).

    Vielleicht möchte ich ja doch ein IST-Ein? Denn ich möchte ja das
    ModeLockedLaser::absorber.N
    ModeLockedLaser::N

    identische Werte haben (was für alle Werte von LaserdynamicBasics gilt).
    Wenn ich dann

    ModeLockedLaser::initSimulation()
    {
    	setSize(1024*4); // set N
    }
    

    aufrufe, rechnet

    ModeLockedLaser::absorber.calc()
    

    ebenfalls mit demselben N.

    Natürlich könnte ich auch ein Objekt 'Common' in ModeLockedLaser haben und den Zeiger darauf an die Konstruktoren von absorber usw. weiterreichen. Aber dann müsste ich ja überall Common.N schreiben. Das wird gruselig im eigentlichen Alorithmencode, und zeigt eher das die abstrakte Aufsplittung Nachteile gebracht hat. Ich würde aber gerne zeigen das es Vorteile bringt.



  • Aber dann müsste ich ja überall Common.N schreiben. Das wird gruselig im eigentlichen Alorithmencode, und zeigt eher das die abstrakte Aufsplittung Nachteile gebracht hat.

    Du setzt "Vorteil" gleich mit "muss ich weniger tippen" bzw. "kann man leichter (schneller) lesen". Das ist manchmal nicht ganz falsch, aber wenn die Struktur bzw. Wartbarkeit des Programms darunter leidet ist es IMO eben doch falsch.

    Wenn der Simulations-Code so "gross" ist dass das ersetzen von "N" durch "simulationParameters->N" so schlimm wäre, dann kannst du ja "N" in den entsprechenden Funktionen als lokale Variable definieren:

    void blah::foo()
    {
        int const N = m_simulationParameters->N;
        ...
    }
    

    Das geht natürlich nur wenn "m_simulationParameters->N" von foo() nicht verändert wird (direkt oder indirekt). Theoretisch könntest du, falls N doch verändert werden muss, statt "int const N" immer noch "int& N = ..." schreiben, allerdings würde ich das nicht machen. Dann lieber einfach überall "m_simulationParameters->N" schreiben, dann sieht man wenigstens was passiert.

    Hier mit Vererbung um sich zu werfen halte ich auf jeden Fall auch für keine gute Idee. Abgesehen davon dass die "EDIT: geht das überhaupt?" Variante eben nicht geht - virtuelle Vererbung greift nur innerhalb der Vererbung-Hierarchie, nicht zwischen Basisklassen und Membern (es wäre für so ziemlich alle Fälle ausser dem den du hier hast auch katastrophal wenn es anders wäre).

    Vielleicht möchte ich ja doch ein IST-Ein? Denn ich möchte ja das
    ModeLockedLaser::absorber.N
    ModeLockedLaser::N

    identische Werte haben (was für alle Werte von LaserdynamicBasics gilt).

    Ich denke du verstehst Vererbung und die "ist ein" Beziehung etwas falsch. Aber naja.


Anmelden zum Antworten