Nach einigen Recherchen kann ich anscheinend kein einfaches Beispiel finden, um ein Problem zu lösen, auf das ich häufig stoße.
Angenommen, ich möchte eine kleine Anwendung erstellen Square
, in der ich s, Circle
s und andere Formen erstellen , sie auf einem Bildschirm anzeigen, ihre Eigenschaften nach Auswahl ändern und dann alle ihre Umfänge berechnen kann.
Ich würde die Modellklasse so machen:
class AbstractShape
{
public :
typedef enum{
SQUARE = 0,
CIRCLE,
} SHAPE_TYPE;
AbstractShape(SHAPE_TYPE type):m_type(type){}
virtual ~AbstractShape();
virtual float computePerimeter() const = 0;
SHAPE_TYPE getType() const{return m_type;}
protected :
const SHAPE_TYPE m_type;
};
class Square : public AbstractShape
{
public:
Square():AbstractShape(SQUARE){}
~Square();
void setWidth(float w){m_width = w;}
float getWidth() const{return m_width;}
float computePerimeter() const{
return m_width*4;
}
private :
float m_width;
};
class Circle : public AbstractShape
{
public:
Circle():AbstractShape(CIRCLE){}
~Circle();
void setRadius(float w){m_radius = w;}
float getRadius() const{return m_radius;}
float computePerimeter() const{
return 2*M_PI*m_radius;
}
private :
float m_radius;
};
(Stellen Sie sich vor, ich habe mehr Klassen von Formen: Dreiecke, Sechsecke, jedes Mal ihre Proprers-Variablen und zugehörige Getter und Setter. Die Probleme, mit denen ich konfrontiert war, hatten 8 Unterklassen, aber für das Beispiel habe ich bei 2 angehalten.)
Ich habe jetzt eine ShapeManager
, die alle Formen in einem Array instanziiert und speichert:
class ShapeManager
{
public:
ShapeManager();
~ShapeManager();
void addShape(AbstractShape* shape){
m_shapes.push_back(shape);
}
float computeShapePerimeter(int shapeIndex){
return m_shapes[shapeIndex]->computePerimeter();
}
private :
std::vector<AbstractShape*> m_shapes;
};
Schließlich habe ich eine Ansicht mit Spinboxen, um jeden Parameter für jeden Formtyp zu ändern. Wenn ich beispielsweise ein Quadrat auf dem Bildschirm auswähle, zeigt das Parameter-Widget nur Square
-bezogene Parameter an (dank AbstractShape::getType()
) und schlägt vor, die Breite des Quadrats zu ändern. Dazu benötige ich eine Funktion, mit der ich die Breite ändern kann ShapeManager
, und so mache ich das:
void ShapeManager::changeSquareWidth(int shapeIndex, float width){
Square* square = dynamic_cast<Square*>(m_shapes[shapeIndex]);
assert(square);
square->setWidth(width);
}
Gibt es ein besseres Design, das mich daran hindert, das dynamic_cast
Getter / Setter-Paar ShapeManager
für jede Unterklassenvariable zu verwenden und zu implementieren ? Ich habe bereits versucht, eine Vorlage zu verwenden, bin jedoch fehlgeschlagen .
Das Problem , das ich bin Bewurf ist nicht wirklich mit Formen , aber mit unterschiedlichem Job
s für einen 3D - Drucker (zB: PrintPatternInZoneJob
, TakePhotoOfZone
, etc.) mit AbstractJob
als ihre Basisklasse. Die virtuelle Methode ist execute()
und nicht getPerimeter()
. Das einzige Mal, dass ich konkrete Verwendung verwenden muss, ist das Ausfüllen der spezifischen Informationen, die ein Job benötigt :
PrintPatternInZone
benötigt die Liste der zu druckenden Punkte, die Position der Zone, einige Druckparameter wie die TemperaturTakePhotoOfZone
benötigt die Zone, in die das Foto aufgenommen werden soll, den Pfad, in dem das Foto gespeichert wird, die Abmessungen usw.
Wenn ich dann anrufe execute()
, verwenden die Jobs die spezifischen Informationen, die sie benötigen, um die Aktion zu realisieren, die sie ausführen sollen.
Das einzige Mal, dass ich den konkreten Typ eines Jobs verwenden muss, ist, wenn ich diese Informationen ausfülle oder anzeige (wenn a TakePhotoOfZone
Job
ausgewählt ist, wird ein Widget angezeigt, das die Parameter für Zone, Pfad und Dimensionen anzeigt und ändert).
Die Job
s werden dann in eine Liste von Job
s eingefügt, die den ersten Job annehmen, ihn ausführen (durch Aufrufen AbstractJob::execute()
), die zum nächsten weitergehen, weiter und weiter bis zum Ende der Liste. (Deshalb verwende ich Vererbung).
Um die verschiedenen Arten von Parametern zu speichern, verwende ich a JsonObject
:
Vorteile: gleiche Struktur für jeden Job, kein dynamic_cast beim Setzen oder Lesen von Parametern
Problem: Zeiger (auf
Pattern
oderZone
) können nicht gespeichert werden
Gibt es Ihrer Meinung nach eine bessere Möglichkeit, Daten zu speichern?
Dann , wie würden Sie die konkrete Art der SpeicherungJob
, es zu benutzen , wenn ich die spezifischen Parameter dieses Typs ändern müssen? JobManager
hat nur eine Liste von AbstractJob*
.
changeValue(int shapeIndex, PropertyKey propkey, double numericalValue)
wo PropertyKey
kann eine Aufzählung oder eine Zeichenfolge sein, und "Breite" (was bedeutet, dass der Aufruf des Setters den Wert der Breite aktualisiert) gehört zu den zulässigen Werten.