2013-02-25 8 views
7

Ich habe mit diesem Designproblem für einige Zeit gekämpft. Ich werde mein bestes geben, um zu erklären, was ich versuche zu tun, und die verschiedenen, die ich gesehen habe, näherten sich, was ich versuche und warum.Lösen von Design mit mehreren Vererbung und zusammengesetzten Klassen in C++

Ich arbeite in einer wissenschaftlichen Computerumgebung, in der ich wiederholt mit den gleichen Arten von Objekten arbeite. Stellen Sie sich eine Galaxie vor, die Sonnensysteme enthält, jedes Sonnensystem enthält Planetensysteme und jedes Planetensystem enthält Monde. Zu diesem Zweck denke ich an die Situation als "hat eine" Situation, und so habe ich Komposition verwendet, um der Galaxie Zugang zu ihren Sonnensystemen zu geben, und jedes Sonnensystem Zugang zu den Planetensystemen, die Zugang zu ihren Monden haben: jede Kategorie eine eigene Klasse sein.

Es ist oft der Fall, dass die verschiedenen Probleme, an denen ich arbeite, verschiedene Arten von Daten über diese Objekte enthalten. Und wenn verschiedene Arten von Daten verfügbar werden, kann ich bestimmte Dinge mit meinen Objekten tun. Also, wenn ich Datentyp 1 zur Verfügung zu mir haben erstelle ich die folgenden Klassen

class GalaxyOne { /* … */ }; 
class SolarSystemOne { /* … */ }; 
class PlanetOne{ /* … */ }; 
class MoonOne{ /* … */ }; 

Und wenn ich Datentyp 2 zur Verfügung zu mir habe ich schaffen

class GalaxyTwo { /* … */ }; 
class SolarSystemTwo { /* … */ }; 
class PlanetTwo{ /* … */ }; 
class MoonTwo{ /* … */ }; 

Die Verbund Aspekte jeder Klasse behandelt werden mit Containervariablen wie Vektoren, die Zeiger auf die enthaltenen Klassen enthalten. Zum Beispiel würde man innerhalb jeder Galaxy-Klasse finden.

class GalaxyTwo{ /* … */ 
protected: 
std::vector<SolarSystemTwo*> solarSystems; 
/* … */ 
}; 

Die Schwierigkeit kommt, wenn ich Klassen in Klassen höherer Ordnung kombinieren möchte.

class GalaxyOneTwo: public GalaxyOne, public GalaxyTwo{ 
/* Additional methods */ 
}; 

Dies schafft jedoch ein Problem der Mehrdeutigkeit in den Solarsystemen Vektor weil GalaxyOneTwo eine Version davon aus GalaxyOne und GalaxyTwo haben würde. Darüber hinaus enthalten die Vektoren, die er erbt, Zeiger auf Objekte, die nicht vom Typ SolarSystemOneTwo sind, die benötigt würden. Also, ich dachte, ich könnte eine Template-Klasse erstellen, die alle meine Objekte erben von wo ich alle meine Container-Variablen.

template<MoonT,PlanetT,SolarSystemT> 
    class PrimitiveGalaxy { 
    private: 
     std::vector<SolarSystemT*> solarSystems 
}; 

class GalaxyOne: public PrimitiveGalaxy <MoonOne,PlanetOne,SolarSystemOne>{ 
    /* No longer defining any container variables for composition */ 
}; 

Dieser Ansatz funktioniert sehr gut für die grundlegenden Galaxy-Typen (GalaxyOne und GalaxyTwo). Wenn ich jedoch versuche, den kombinierten Galaxietyp zu erstellen, bekomme ich alle Arten von Zweideutigkeiten.

class GalaxyOneTwo: public GalaxyOne, public GalaxyTwo, public  PrimitiveGalaxy<MoonOneTwo,PlanetOneTwo,SolarSystemOneTwo>{ 
    /* … */ 
}; 

ich Mehrdeutigkeit, wenn ich innerhalb eines solarsystems-Methode definiert in GalaxyOneTwo verwenden, da es dreimal definiert ist, einmal durch jede geerbte Version von GalaxyOne und GalaxyTwo und ein drittes Mal durch GalaxyOneTwo.

I diese Zweideutigkeit bekommen kann befreien, indem spezifische und mehr

PrimitiveGalaxy :: Solarsysteme

jedes Mal auf den richtigen Vektor mit Bezug zu nehmen, aber dies ist nicht wünschenswert, da es eine Menge zusätzlicher Eingabe erfordert und Syntax.

Ich habe überlegt, Freundschaft zwischen jedem Galaxietyp und der primitiven Galaxie zu verwenden, aber dies erfordert eine ähnliche Ebene des ausführlichen Schreibens.

Ich habe das Gefühl, dass Namespaces mein Schreiben von Code vereinfachen kann, aber ich bin nicht sicher, wie Sie den Namespace zu definieren, so dass innerhalb eines Namespace für GalaxyOneTwo definiert, dass jede Bezugnahme auf solarsystems ist ein Verweis auf PrimitiveGalaxy :: Solarsysteme

Bearbeiten:

Bitte beachten Sie, der einzige Unterschied zwischen GalaxyOne und GalaxyTwo ist nicht der Typ der Klasse in SolarSystems enthalten. Es gibt viele Unterschiede, da jede Klasse sich mit verschiedenen Daten befasst, die für die Galaxie relevant sind. Daher erstelle ich verschiedene Klassen, die verschiedene Zustandsvariablen, Getter und Setter für diese Zustandsvariablen und Methoden zum Berechnen und Drucken von Daten haben. solarSystems ist ein Beispiel für die Funktion, die mir Probleme bereitet, so wie ich es hier beschrieben habe. Wenn GalaxyOneTwo erstellt wird, wird es dieselben Daten verwenden, die für GalaxyOne und für GalaxyTwo verwendet werden, und daher möchte ich alle ihre Variablen und Methoden erben. Und weil die Daten auf verschiedene Arten kombiniert werden können, muss ich dafür innerhalb von GalaxyOneTwo neue Methoden entwickeln. Dies sind einige der vielen Unterschiede, die mich dazu führen, Vererbung zu verwenden. Davon abgesehen sind es die Containervariablen, die eine Komposition ermöglichen, die mir Probleme bereiten. Innerhalb jeder solarSystem-Klasse wird es einen ähnlichen Vektor geben, der ihnen Zugang zu ihren Planeten gibt, und so weiter und so fort.

Edit:

Für eine Frage speziell auf meine Design-Philosophie gewidmet hier im Allgemeinen (im Gegensatz zu diesen Fragen Schwerpunkt im Gegensatz zu versuchen, meinen aktuelles Design Versuch zu lösen) ist unter folgendem Link: Guidance in creating design for multiple-inheritance composite classes in c++

+0

Was soll ein GalaxyOneTwo sein? Eine Sammlung von verschiedenen Arten von Galaxien oder eine gemeinsame Schnittstelle (oder Implementierung) der beiden? Welche Art von Operationen möchten Sie auf einem Galaxy One tun? –

+0

Wie ich oben beschreiben wollte, ist GalaxyOneTwo eine Galaxie, die nicht nur Zugriff auf die Elementfunktionen von GalaxyOne und GalaxyTwo hat, sondern auch die Möglichkeit bietet, die gleichen Daten zu verarbeiten nur zu GalaxyOne und GalaxyTwo, um kompliziertere Datenanalyse durchzuführen. – PhiloEpisteme

+0

Nein, ich frage mich: Was ist das, was Sie versuchen, mit Vererbung zu modellieren? Eine Art Galaxie mit allen Arten von Sonnensystemen in Galaxie eins und Galaxie zwei? Wenn ich nach der Art von Operationen frage, meine ich ein konkretes Beispiel, wie zB die Gesamtmasse der Galaxie, Leuchtkraft, Sternenzählung und so weiter. Wenn Sie Beispiele dafür geben, welche Art von kombinierten Funktionen GalaxyOneTwo haben sollte, können Sie bessere Antworten erhalten. –

Antwort

1

Vielleicht könnten Sie Datenmodelle ändern, um etwas Komponentenbasiertes zu implementieren. Jede Komponente könnte dieselben Zustandsinformationen enthalten, die Sie für die verschiedenen Typen angegeben haben. Und könnten Sie virtuelle Funktionen erben

class Galaxy 
{ 
    // galaxy information 

    // Virtual functions shared among galaxies 
    virtual void sharedGalaxyFunction() = 0; 

    // Vector containing all solar systems in this galaxy 
    std::vector<SolarSystem*> solarSystems_; 
}; 

class SolarSystem 
{  
    // solar system info 

    // Virtual functions shared among all solar systems 
    virtual void sharedSolarSystemFunction() = 0; 

    // Vector containing planets 
    std::vector<Planets*> planets_; 
}; 

// etc for planets... 

Sie könnten dann die verschiedenen Arten von Solaranlagen erben oder Galaxien, die Sonderfälle zu erstellen und die virtuelle Funktion ausfüllen ruft

class CoolSolarSystem : public SolarSystem 
{ 
    // Special variables 

    // Fill in the virtual function 
    void sharedSolarSystemFunction(); 
}; 

Sie dann die Behälter füllen könnte in den verschiedenen Basistypen mit den Hinweisen zu Ihren speziellen Typen. Sie sollten in der Lage sein, eine Rückübertragung auf den speziellen Typ zu vermeiden, wenn die Aufrufe der virtuellen Funktion genügend Informationen verarbeiten.

+0

Müsste dieser Ansatz nicht viele Funktionen neu definieren, wenn kompliziertere SolarSystem-Klassen erstellt werden, wenn neue Daten verwendet werden? – PhiloEpisteme

+0

Funktioniert das, wenn nur einige CoolSolarSystems die gemeinsame Funktion implementieren? Was ich mir vorstelle, ist, dass ich, wenn ich mehr zusammengesetzte Funktionen erstelle, die spezialisierten Funktionen so definiere, wie ich sie in den spezialisierten Funktionsfällen benötige, und dann in der Klasse SolarSystem eine virtuelle Funktion hinzufüge, die später definiert wird. Aber für Methode 1 sagt nur CoolSolarSystem1 diese Funktion. Ist es in Ordnung, wenn CoolSolarSystem2 es undefiniert lässt? – PhiloEpisteme

+0

Wenn die Funktion keine reine virtuelle Funktion ist, dann könnte das definitiv funktionieren. Sie müssten einige einfache Funktionen für die Basisklasse implementieren oder sie einfach leer lassen, wenn die Funktion nichts für "nicht implementierte" Objekte tun soll. –

2

I Denken Sie, Sie müssen eine haben class Galaxy, eine class SolarSystem, usw. Und Ihre GalaxyOne, GalaxyTwoSolarSystemOne, SolarSystemTwo usw. sind nur verschiedene Objekte instanziiert von diesen Klassen.

class SolarSystem { /* … */ }; 
class Planet{ /* … */ }; 
class Moon{ /* … */ }; 

class Galaxy{ /* … */ 
public: // Galaxy(...?...){for (...) {read data??; solarSystem.push_back(createSolarSystem(data)); } 
void AddSolarSystem(SolarSystem* ss){solarSystem.push_back(ss);} 
protected: 
    std::vector<SolarSystem*> solarSystems; 
    /* … */ 
}; 

....

Galaxy GalaxyOne, GalaxyTwo; 

Wenn wir keine Möglichkeit haben, diese einfache aproach zu verwenden ... Lets Ihr sehen:

class GalaxyOneTwo: public GalaxyOne, 
        public GalaxyTwo, 
        public PrimitiveGalaxy<MoonOneTwo,PlanetOneTwo,SolarSystemOneTwo>{ 
    /* … */ 
using PrimitiveGalaxy<MoonOneTwo,PlanetOneTwo,SolarSystemOneTwo>::solarSystems; 
GalaxyOneTwo(){solarSystems.reserve(10);} 
}; 

Hier können Sie drei private Vektoren haben: (mit using machen Sie es direkt zugänglich)

std::vector<SolarSystemOne* > GalaxyOne::solarSystems; 
std::vector<SolarSystemTwo* > GalaxyTwo::solarSystems; 
std::vector<SolarSystemOneTwo*> solarSystems; //GalaxyOneTwo::solarSystems; 

Benötigen Sie das? Machen Sie es geschützt? enter image description here

+0

Nein, das ist nicht der Fall. Vielleicht war ich nicht klar aus meiner Beschreibung. Ich beschäftige mich mit verschiedenen Arten von Daten, die verschiedene Variablen und Methoden erfordern, um mit ihnen zu arbeiten. GalaxyOne ist eine Klasse von Galaxien, die entwickelt wurde, um bestimmte Daten zu verarbeiten.GalaxyTwo behandelt einen anderen Datensatz und verfügt daher über verschiedene Variablen und Methoden zur Datenverarbeitung. GalaxyOneTwo ist, wenn ich beide originalen Arten von Daten habe und somit die originalen Variablen und Methoden von GalaxyOne und GalaxyTwo auch als neue Variablen und Methoden die Synthese der Daten zur Hand haben wollen. – PhiloEpisteme

+0

Ja, ich habe eine Frage: Die Art des SolarSystems ist der einzige Unterschied zwischen den Galaxys Typen? GalaxyOneTwo wird nur ein SolarSystem der Klasse Eins und andere vom Typ Zwei haben? – qPCR4vir

+0

Nein, das ist nicht der einzige Unterschied. Wo Sie die Ellipse sehen, ist wo andere Unterschiede sind. Es gibt signifikante Unterschiede. GalaxyOne beschäftigt sich mit einer Reihe von Daten, Anruf ist data1. Es gibt Variablen und Methoden zu Daten1, die Sie in GalaxyOne finden. GalaxyTwo beschäftigt sich mit einem Datensatz, data2, der nicht mit dem von data1 identisch ist. Somit hat GalaxyTwo viele weitere Unterschiede. Ich habe keine Schwierigkeiten mit diesen Variablen und Methoden, die keine Container-Variablen sind, die Objekte wie SolarSystemOne oder MoonOne usw. enthalten, also habe ich diese nicht aufgenommen. – PhiloEpisteme

0

Sie aus der gemeinsamen Sache zwischen dem ‚One‘ und ‚Zwei‘ Versionen von Sachen in eine Reihe von ‚gemeinsamen‘ Basisklassen zu abstrahieren müssen:

class GalaxyCommon { 
    // all the common stuff between GalaxyOne and GalaxyTwo including 
    std::vector<SolarSystemCommon *> solarSystems; 
    ... 
}; 

class GalaxyOne : public virtual GalaxyCommon { 
    // all the stuff that is unique to model One 
}; 

class GalaxyTwo : public virtual GalaxyCommon { 
    // all the stuff that is unique to model Two 
}; 

class GalaxyOneTwo : public virtual GalaxyOne, public virtual GalaxyTwo { 
    // should be complete 
}; 

Beachten Sie die Verwendung von virtual - erforderlich, um die Mehrfachvererbung korrekt zu bearbeiten.

+0

Ich habe bereits die üblichen Sachen abstrahiert, daher die Verwendung von PrimitiveGalaxy. Mein Problem ist in der Tat nicht eines der Diamant-Problem, weil PrimitiveGalaxy eine Vorlagenklasse ist und jedes Mal, wenn es von jeder Klasse geerbt wird, eine andere Version davon für die relevanten Vorlagen erstellt wird. Meine Herausforderung besteht darin, einen effizienten Weg zu finden, die Mehrdeutigkeit in Bezug auf die Mitglieder von PrimitiveGalaxy von GalaxyOneTwo zu entfernen. – PhiloEpisteme

0

Also, wenn ich Ihre Frage richtig verstanden:

  1. Es gibt mehrere Arten von Galaxien, die gar nichts gemeinsam zwischen ihnen haben.
  2. Das gleiche gilt für Solar- und Planetensysteme.
  3. Sie möchten Sammlungen dieser verschiedenen Typen kombinieren und haben dennoch vollen Zugriff auf die zugrunde liegenden Methoden für diesen Typ.

Das klingt wie ein Fall für boost::any

So jetzt Ihre Galaxy ist:

#include <list> 
#include <boost/any.hpp> 
typedef std::vector<boost::any> collection; 
class Galaxy 
{ 
    collection SolarSystems; 
    public: 
    ...methods... 
}; 
class SolarSystem 
{ 
    collection PlanetarySystems; 
    public: 
    ...methods... 
}; 
class PlanetarySystem 
{ 
    collection Moons; 
    public: 
    ...methods... 
}; 

Ofcourse, das ist wirklich nicht alles tun, aber tut man sie zu einer Gruppe ermöglichen. Um etwas Nützliches zu tun (Methode aufrufen), müssen Sie immer noch den Typ erkennen, ihn auf diesen Typ umwandeln und dann den Code ausführen, den Sie benötigen.

Was Sie jedoch tun können, ist Objekte verschiedener Typen zu gruppieren, was ich mit GalaxyOneTwo denke. Hier ist more information auf wie Sie das tun können.