Ahoi
-
jetzt kratze ich mich doch etwas am Kopf.
>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 bin verwirrt, kann aber auch am Rotwein um diese Uhrzeit liegen....
Kannst Du mal den vollständigen Sourcecode anhängen? Da fehlt einiges an
Infos. (Da fehlt doch so etwas wie templates...?)
Jasson J. schrieb:> 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:
> Zum Thema OOP generell:>> 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.
Klaus H. schrieb:> (Da fehlt doch so etwas wie templates...?)
Ah ja, meine Schreibweise mit < > war nicht clever. Damit meine ich
nicht Templates sondern, Platzhalter für die Kanalfarbe oder Funktion.
Davon
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.
Um das Beispiel mal zu übernehmen
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.
Jasson J. schrieb:> 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
Richtig, es hat. Bei deiner Umsetzung ist ein Gerät aber
unterschiedliche Kanäle. Das ist genau das, was hier gemeint war:
Mark B. schrieb:> 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.
Rolf M. schrieb:> 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?
Der Nutzer / Consumer der Lampen wäre etwas, dass ich inzwischen
"Korrespondenz" taufen würde - etwas das einen Eingangswert auf ein oder
mehrere Ausgangswerte Abbildet.
Beitrag "Re: Welches mathematisches Konzept bildet 1 Eingangswert auf 1+n Ausgangswerte ab?"
Könnte z.B. das klassische Farbrad sein, dass 0-255 auf Werte für
Rot-Grün-Blau abbildet
-
und diese dann über ein Interface I_RgbDevice in alle Geräte spült, die
sich unter dem Interface angemeldet haben.
Dann könnte ein RGB-W Gerät z.B. verschiedene Interfaces unterstützen
I_RGB
I_WhiteSpectrum
I_WhiteOnly
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.
Rolf M. schrieb:> 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.
Hi,
als jemand der sowohl Software schreiben kann als auch mit Lichtpulten
umgehen kann:
Dein Ansatz hat einige fundamentale Probleme.
1) 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.
2) 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).
Hi,
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 ;)
Viel Erfolg!