OOP-Style: Tasks verarbeiten
-
Hallo. Folgende Situation: Ich habe verschiedene Klassen, die klassenspezifische Tasks nacheinander verarbeiten sollen.
- Jeder Task benötigt eine gewisse Zeitdauer und hat weitere Kosten (int-Werte)
- Vor dem Hinzufügen der Tasks würde ich gerne prüfen, ob der Nutzer sich den Task leisten kann
Mein derzeitiger Ansatz wäre der Folgende (ungetestet aus dem Kopf getippt):
class Task { virtual int duration_ms() = 0; virtual int costs() = 0; }; class ObjectA { public: // Klassenspezifische Tasks class TaskUpgrade1 : public Task { // implementierung der virtual Funktionen }; class TaskUpgrade2 : public Task { // implementierung der virtual Funktionen }; // Könnte man auch noch verallgemeinern void addTask(Task* pTask) { if(typeid(*pTask) == typeid(TaskUpgrade1) || typeid(*pTask) == typeid(TaskUpgrade2)) { m_vecTasks.push_back(pTask); } private: std::vector<Task*> m_vecTasks; // Könnte man auch noch verallgemeinern }; class ObjectB { public: // Klassenspezifische Tasks class TaskProduceThis: public Task { // implementierung der virtual Funktionen }; class TaskProduceThat: public Task { // implementierung der virtual Funktionen }; class TaskUpgrade: public Task { // implementierung der virtual Funktionen }; // Könnte man auch noch verallgemeinern void addTask(Task* pTask) { if(typeid(*pTask) == typeid(TaskProduceThis) || typeid(*pTask) == typeid(TaskProduceThat) || typeid(*pTask) == typeid(TaskUpgrade)) { m_vecTasks.push_back(pTask); } private: std::vector<Task*> m_vecTasks; // Könnte man auch noch verallgemeinern };
In der Verarbeitungsmethode der Klasse(n) würde dann wieder geprüft werden, was aktuell für ein Task vorliegt und dann anhand der Zeit&Kosten verarbeitet werden. Alternativ könnte man die Prüfung beim hinzufügen weglassen, da die Klasse später eh nochmal prüft, was für ein Task aktuell anliegt. Unbekannte könnten dann ignoriert werden (default: break).
Ich bin aber noch nicht ganz glücklich damit. Wie würdet ihr das lösen?
-
Nachtrag:
Ein Nachteil wäre irgendwie die Benutzung. Angenommen der Benutzer klickt einen Button für TaskProduceThat:
onButton_TaskProduceThat: ObjectB::TaskProduceThat task; if(task.costs() < availableResources()) { availableResources -= task.costs(); pObjectB->addTask(new ObjectB::TaskProduceThat()); }
Man sieht, die Benutzung ist recht holprig. Wenn der Benutzer z.B. den Task abbrechen will, soll er auch die Kosten wiederbekommen
-
Mich stören vor allem zwei Dinge:
- Benutzung von rohen, besitzenden Zeigern.
std::unique_ptr
ist dein Freund. - die
typeid
Überprüfungen. Müssen Task heterogen verwaltet werden (leben Tasks von ObjectA und ObjectB in einem Container)? Wenn ja würde ich eine Hierachiestufe zwischenTask
und den konkreten Tasks einfügen:
Task +--------+--------+ TaskA TaskB +------+------+ TaskBThis TaskBThat
Damit kann die Signatur auf z.B.
ObjectB::addTask( TaskB* Task )
geändert werden.Ansonsten würde ich auf
Task
verzichten und nurTaskA
undTaskB
benutzen. Hängt jedoch von der Zahl der Basisklassen ab. Wieviele A, B, ... gibt es denn?
- Benutzung von rohen, besitzenden Zeigern.
-
Es gibt so ca. 14 unterschiedliche Objekte, wobei einige kaum Tasks verarbeiten, anderen wiederum recht viele.
Die Idee mit der Hierarchiestufe ist schon einmal sehr gut. Was mich interessieren würde: Würdest du das gänzlich anders lösen? Ich bin da echt offen für Ansätze