Möglichst kurz zum Kontext
Ich versuche DMX orientiert Geräte zu abstrahieren. Dazu versuche ich mich erstmal am naheliegendsten, einem RGB Strahler.
Mein Ziel ist, dass das virtuelle Gerät direkt über seine Instanz mittels
.SetRed(value); etc ansprechbar ist, die Referenz aber auch in einer abstrakteren Variable wie RGB_Device gehalten werden kann.
Meine Idee war, zu jeder Farbe gibt es eine pur virtuelle Klasse á la
Das Interface für das RGB_Device klebe ich auch aus den einzelnen Farben zusammen. Ich denke, bei diesem konkreten Beispiel würde es noch ohne Interface gehen, aber vielleicht will man das selbe Hardware-Gerät als RGB+Weiß darstellen.
Ich versuche DMX orientiert Geräte zu abstrahieren. Dazu versuche ich
mich erstmal am naheliegendsten, einem RGB Strahler.
Meine Idee war, zu jeder Farbe gibt es eine pur virtuelle Klasse á la
Zum Thema OOP generell:
Du willst ein Gerät in Software modellieren. Dieses Gerät ist ein RGB-Strahler.
Das Gerät hat nun zum Beispiel eine rote, eine blaue und eine grüne LED verbaut. Das ist eine hat ein Beziehung.
Wenn man objektorientiert vernünftig modellieren will, sollte man die "ist ein" und die "hat ein" Beziehungen vernünftig auseinander halten. Das sind zwei verschiedene Dinge.
Die konkrete Implementierung habe ich so versucht:
class RGB_Device : public Red_8Bit, public Green_8Bit, public Blue_8Bit, public I_RGB_Device
Ein Gerät von einzelnen Komponenten abzuleiten ergibt nicht viel Sinn. Stattdessen kann Dein Gerät die Komponenten als Member-Variablen beinhalten, zum Beispiel so:
Du willst ein Gerät in Software modellieren. Dieses Gerät ist ein
RGB-Strahler.
...
Das war (oder ist) letztlich auch mein Ziel. Ich hatte die Idee, feingranulare - also im Prinzip einkanalige - Geräte zu haben, aus denen ich wie mit
KLEMMBAUSTEINEN (nicht Lego :>) größere Geräte zusammensetzten kann und dabei schematisch gleichen Code vermeiden möchte. Ich häng mal ein Bild an.
Mit einer Hat-ein Beziehung müssten die Methoden die zu einer virtuellen Repräsentation passen noch mal widerholt werden. Zwar den Code nicht, aber die Bodys müssten noch mal getippt werden und darin auf die passenden Member zugegriffen werden.
Warum sollten verschiedene Farben eigene Klassen sein? Das macht gar keinen Sinn. Auch ein Template nicht weil die Farben nicht unterschiedliche Typen sind.
Also so wie Mark es schon vorgeschlagen hat.
Ich bräuchte neben der Aussage, 'dass' es keinen Sinn macht eine Begründung, um zu verstehen, 'warum' es keinen Sinn macht.
Umgekehrt habe ich "ja" eine Begründung, warum ich es - zumindest aktuell - sinnvoll finde:
Ein physisches Gerät Typ_A hat Kanäle
rot
grün
blau
Pan
Ein physisches Gerät Typ_B hat Kanäle
rot
grün
blau
weiß
amber
UV
Dimmer
Pan
Tilt
dann macht es in meinen Augen Sinn, die einzelnen Kanäle quasi wie einzelne Klötzchen über Ist-ein zu einem gesamten zusammensetzen zu können und damit direkt die jeweiligen Methoden zu haben. Mit einer Hat-ein Beziehung müsste ich bei jedem Gerätetypen die Methoden der Kanäle erneut abtippen um sie nach außen nutzbar zu machen, obwohl der Code immer der gleiche ist.
Der Unterschied zwischen den drei Kanälen R, G und B ist irrelevant. Die drei verhalten sich exakt identisch - nur die LED, die hinten dranhängt, macht was anderes.
Es hat hier auch keinen Sinn, eine LED-Basisklasse zu verwenden und die Farb-LED-Klassen davon abzuleiten, denn was machen die unterschiedliches? Nichts. Der einzige Unterschied liegt in der Hardware.
Wenn man objektorientiert vernünftig modellieren will, sollte man die
"ist ein" und die "hat ein" Beziehungen vernünftig auseinander halten.
Das sind zwei verschiedene Dinge.
rot
grün
blau
Pan
Aber warum müssen diese Kanäle von unterschiedlichen Klassen sein? Unterschiedliche Klassen macht man, weil sie sich unterschiedlich verhalten. Das Verhalten der Klasse hängt aber nicht von der Farbe ab. Diese ist eher ein Attribut.
Ein physisches Gerät Typ_B hat Kanäle
rot
grün
blau
weiß
amber
UV
Dimmer
Pan
Tilt
dann macht es in meinen Augen Sinn, die einzelnen Kanäle quasi wie
einzelne Klötzchen über Ist-ein zu einem gesamten zusammensetzen zu
können und damit direkt die jeweiligen Methoden zu haben.
Ich würde da eher eine einzige Klasse für alle Farben machen und dann dem Strahler z.B. ein Array/vector oder eine Map aus Kanälen mitgeben. Du musst dir dann nur eine gute Möglichkeit überlegen, wie man eine Farbkomponente gezielt auswählt.
Mit einer Hat-ein Beziehung müsste ich bei jedem Gerätetypen die Methoden
der Kanäle erneut abtippen um sie nach außen nutzbar zu machen, obwohl
der Code immer der gleiche ist.
Du brauchst eigentlich nur eine Funktion, der du halt noch sagst, welchen Kanal du jetzt ansprechen möchtest. Getrennte Funktionen für jede Farbe halte ich nicht für sinnvoll.
Alternativ könnte dein Strahler eine Funktion haben, mit der du dir eine Referenz auf den gewünschten Kanal geben lassen kannst. Und den kannst du dann einfach über seine eigenen Memberfunktionen verändern.
Aber warum müssen diese Kanäle von unterschiedlichen Klassen sein?
Unterschiedliche Klassen macht man, weil sie sich unterschiedlich
verhalten. Das Verhalten der Klasse hängt aber nicht von der Farbe ab.
Diese ist eher ein Attribut.
Das könnte der springende Denkanstoß sein
Das stimmt, meine Kanalklassen verhalten sich identisch. Die Idee einem identischen Verhalten verschiedene Typen zu geben war, darüber die Implementierung von Interfaces mit zu liefern, damit sich z.B. jedes Gerät, dass von Rot-Grün-Blau geerbt hat, bei einem Consumer als I_RGB_Device anmelden kann.
ist oft schwierig die Balance zu finden, zwischen das konkrete Problem das man hat zu beschreiben und den ganzen Rest drum rum, warum man es "so" lösen will. Das blurt die eigentliche Frage oft aus.
Aber wie gesagt - über den Denkanstoß denke ich nach
Mir hilft es oft, den nächst höheren Level zu betrachten:
Wer arbeitet mit dieser Klasse/Objekt?
Wie einfach/komplex wird die Nutzung?
Dann merkt man schnell, dass das Interface vielleicht unten super einfach zu erstellen war, aber darüber die Probleme erst beginnen.
Wie würdest du die konkreten Lampen/Instanzen (z.B. aus einer Liste mit 3x RGB, 2*RGBW, 4x RGBWA) dann nutzen? Wie erkennt die Applikation, dass Device2 aus der großen Liste 2 Attribute mehr hat als Device1?
Grundsätzlich stellt sich, wie oben von den anderen bereits erwähnt, wofür du die Abstraktion brauchst. So wie ich das verstehe, möchtest du quasi ein [https://refactoring.guru/design-patterns/strategy](Strategy Pattern) implementieren für alle denkbaren Kombinationen von Sensoren.
Das hat mit Vererbung auch tendenziell eher wenig zu tun (btw: deine Interface-Klassen benutzen finale Destruktoren in der Basisklasse -> possible Memleak), sondern ist die vtable ja bloß ein Mittel zur aktuellen Methode: du rufst ja nicht wirklich Basisklassencode auf.
Die Idee ist finde ich erstmal nicht verkehrt, allerdings kommst du mit einem zu generischen Ansatz schnell in Teufels Küche. Such dir lieber 3-4 konkrete Aktoren heraus, implementier diese erstmal im Code aus und such dann erst wenns schon funktioniert nach einer Abstraktion.
Richtig, es hat. Bei deiner Umsetzung ist ein Gerät aber
unterschiedliche Kanäle.
Das würde ich jetzt nicht so unterschreiben. Er implementiert Interfaces. Die Klassen gibt es nur, weil C++ halt keine echten Interfaces hat.
Wenn ich bei einer Klasse Dog und einer Klasse Cat ein interface CanMakeNoise Implementiere, dann ist das aus Design technischer Sicht keine ist ein und keine hat ein Beziehung. Man markiert lediglich alle Objekte die bestimmte Eigenschaften / Aktionen haben. Ich weiss gerade nicht, wie man das Pattern nennt, aber z.B. in Java ist es nicht unüblich.
Sinnvoll ist es hier vermutlich aber trotzdem nicht.
als jemand der sowohl Software schreiben kann als auch mit Lichtpulten umgehen kann:
Dein Ansatz hat einige fundamentale Probleme.
Du willst eigentlich deine Lampenbeschreibung nicht hart reinprogrammieren sondern dynamisch aus Configdateien / Datenbanken laden. Sonst musst du ja für jeden neuen Scheinwerfertyp die Software ändern?
Das mag je nach Anwendungsfall noch ok sein.
Du musst dir ein Parameter-System ausdenken das flexibler ist. deine Klasse sollte nicht .setRed() .setGreen() und .setBlue() haben. Was machst du, wenn du plötzlich ein Gerät mit mehreren Zellen hast und jede Zelle R/G/B/W/A/UV-LEDs hat?
Um flexibel zu bleiben brauchst du eine Geräte-Klasse, die für sich eine Datenstruktur aus Kanälen beinhaltet. Ein Kanal wäre dann z.B. ein 8-Bit-Dimmer, ein 16-Bit-Dimmer, ein Rotwert, ein Grünwert u.s.w.
Damit kannst du dann dann auch kompliziertere Gerät abbilden.
Und anstatt Setter für R-G-B machst du lieber eine Funktion, die einen RGB-Wert nimmt und selber in verschiedene Farbsysteme umrechnet. Es gibt z.B: auch oft Lampen mit CMY-Mischer statt RGB (alles, was eine Weißlichtquelle hat und mehr Optionen bietet als ein einfaches Farbrad).
nicht böse gemeint aber dein mentales Modell der involvierten Programmierkonzepte ist zu schlecht um aus dem Stand eine Abstraktion für sowas hinzuschreiben. Was du da vorschlägst ergibt wenig Sinn.
Ich empfehle stattdessen: schreib' erstmal konkret, auf die einfachste mögliche Weise, also ohne irgendeine vermeidbar Abstraktion, ein paar deiner Geräte so auf, dass es funktioniert. Benutze dazu erstmal ganz einfache Konzepte wie freie Funktionen (aber sparsam! nicht tausend Stück) mit Argumenten und Rückgabewerten, enums, und switch/case und if/else zur Unterscheidung der Geräte, wenn nötig. Keine Klassen, keine Vererbung, keine Templates, keine virtuals.
Dann, wenn dieser einfache Code mal 500+ Zeilen hat, schaust du mal drauf und suchst nach dem Muster, was man am einfachsten da rausabstrahieren könnte. Dafür überlegst du dir dann unter Anwendung der bisher vermiedenen Konzepte eine geeignete Lösung.
Schreib den Code auf die dümmste mögliche Art stur untereinander und abstrahiere nur, wenn du dir wirklich sicher bist dass es diesen dümmsten möglichen Code deutlich vereinfacht.
Nicht umgekehrt alle Konzepte die du irgendwo mal gehört hast draufwerfen in der Hoffnung dass es besser wird. Wird's nicht. Viel besser zu wenig abstrahiert als falsch.
Wenn man viel Erfahrung in Softwareentwicklung und Erfahrung in der Domain (Beleuchtung) hat, kann man auch "aus dem Stand" direkt Abstraktionen erraten, die vermutlich gut sind. Du kannst das nicht. Das macht aber nichts -- außer du versucht es trotzdem ;)