Ich schreibe ein Tool, das es einem Benutzer ermöglicht, mit ein wenig Hardware zu interagieren, indem Einstellungen geändert und dann Informationen gestreamt werden.Wie kann ich Downcasting verhindern, wenn Informationen über eine Warteschlange übertragen werden?
Dazu habe ich ein paar Threads ausgeführt: EquipmentInterface
und DataProcessor
die durch eine Queue
verbunden sind.
Die EquipmentInterface
Gewinde Methoden Einstellungen am Gerät (Rotate
und Refocus
zum Beispiel) zu ändern und die sich ergebende Information (CurrentAngle
und CurrentFocalDistance
) zum Queue
hinzugefügt wird. Sobald die Einstellungen korrekt sind, gibt es Methoden zu StartStreaming
und StopStreaming
und sobald Streaming beginnt, werden Daten von der Ausrüstung paketiert und in die Warteschlange aufgenommen.
Alle Informationen in der Warteschlange stammen von einer einzelnen Klasse BaseMessage
, die eine Angabe des Nachrichtentyps enthält. Ich habe dann Nachrichtentypen für Winkel, Brennweiten, Anfangs- und End-Streaming und natürlich die Daten selbst abgeleitet.
Die DataProcessor
hört auf das andere Ende der Warteschlange und verarbeitet abhängig von der aktuellen Winkel/Brennweite die nachfolgenden Daten.
Nun, die Sache ist, ich habe eine Funktion im Datenprozessor, der eine switch-Anweisung verwendet, um die eingehenden Nachrichten type-check zu überprüfen. Diese Nachrichten werden dann auf den entsprechenden Typ umgewandelt und an einen geeigneten Handler übergeben. In Wirklichkeit gibt es mehr als nur einen DataProcessor, der einer einzelnen Warteschlange zuhört, sondern tatsächlich mehrere Listener in mehreren Warteschlangen (einige speichern auf der Festplatte, andere zeigen Informationen auf einer GUI an). Jedes Mal, wenn ich einige Informationen hinzufüge, muss ich eine neue BaseMessage
abgeleitete Klasse erstellen, einen neuen Typ zu dieser Basisklasse hinzufügen und dann die Switch-Anweisungen in jedem der Konsumenten aktualisieren, um mit der neuen Nachricht fertig zu werden.
Etwas über diese Architektur fühlt sich falsch an und ich habe in letzter Zeit eine Menge über Down-Casting gelesen. Nach dem, was ich gesehen habe, scheint der allgemeine Konsens der what I'm doing is a bad code smell zu sein. Ich habe gesehen a suggestion which use Boost, aber sie sehen nicht sauberer als die switch-Anweisung zu mir (vielleicht fehlt mir etwas?).
Also meine Frage ist: Sollte ich versuchen, die Switch-Statement/Downcasting-Lösung zu vermeiden und wenn ja, wie?
Meine Implementierung ist in C++/CLI, also bin ich entweder mit .net oder C++ - Lösungen.
bearbeiten - Basierend auf den Kommentaren von iammilind und stfaanv, das ist das, was Sie vorschlagen:
class QueuedItem
{
public:
QueuedItem() { }
virtual ~QueuedItem() { }
};
class Angle : public QueuedItem
{
public:
Angle() {}
virtual ~Angle() { }
};
class FocalLength : public QueuedItem
{
public:
FocalLength() {}
virtual ~FocalLength() { }
private:
};
class EquipmentHandler
{
protected:
virtual void ProcessAngle(Angle* angle) {};
virtual void ProcessFocalLength(FocalLength* focalLength) {};
public:
void ProcessMessages(QueuedItem* item)
{
Angle* pAngle = dynamic_cast<Angle*>(item);
if(pAngle != NULL)
{
ProcessAngle(pAngle);
}
FocalLength* pFocalLength = dynamic_cast<FocalLength*>(item);
if(pFocalLength != NULL)
{
ProcessFocalLength(pFocalLength);
}
}
};
class MyDataProcessor : public EquipmentHandler
{
protected:
virtual void ProcessAngle(Angle* angle) override { printf("Processing Angle"); }
virtual void ProcessFocalLength(FocalLength* focalLength) override { printf("Processing FocalLength"); };
};
int _tmain(int argc, _TCHAR* argv[])
{
// Equipment interface thread...
FocalLength* f = new FocalLength();
QueuedItem* item = f; // This gets stuck onto the queue
// ...DataProcessor thread (after dequeuing)
QueuedItem* dequeuedItem = item;
// Example of a DataProcessor implementation.
// In reality, this would
MyDataProcessor dataProc;
dataProc.ProcessMessages(dequeuedItem);
return 0;
}
... und kann vereinfacht werden? Die ProcessMessages
fühlt sich ein wenig klobig an, aber das ist die einzige Möglichkeit, die ich ohne eine switch-Anweisung und eine Art enumerierter Nachrichtentypkennung in der Basisklasse sehen könnte.
Es scheint, als ob Sie dringend * dynamische Polymorphie und dynamischen Versand * brauchen. –
Warum deklarieren Sie eine "virtuelle" Funktion in einer Basisklasse und implementieren Sie nicht alle untergeordneten Elemente. – iammilind
@iammilind: Das würde sicher retten, die switch-Anweisung mehrmals zu implementieren, aber sie wird es nicht vollständig los. Ist das der effizienteste Weg zu bestimmen, welcher Handler angerufen werden soll? –