Hallo,
ich arbeite zur Zeit an einem Farb-Tool, das z.B. Webdesignern die einfache Arbeit mit Farben und die Farbwahl- bzw. Suche erleichtert.
Es sollen Methoden zur Konvertierung von Farben verschiedener Farbräume (RGB, HSV) zur Verfügung gestellt werden. Weiterhin soll es möglich sein, andere "Utilities" zu erstellen, beispielsweise zum Auffinden der Komplementärfarbe, zum Abdunkeln / Aufhellen einer Farbe usw. ...
Meine ersten Überlegungen sehen strukturmäßig wie folgt aus - gibt es dagegen etwas einzuwenden? Ich habe mich an der ToString()
-Methode orientiert und dem IColor
-Interface deshalb eine ToColor()
-Methode hinzugefügt, bin mir aber nicht sicher.
m0rius
Mein Blog: blog.mariusschulz.com
Hochwertige Malerarbeiten in Magdeburg und Umgebung: M'Decor, Ihr Maler für Magdeburg
Ein kleines Problem:
Interfaces in ein Struct zu implementieren kann zum Boxing des Value-Types (Deine Color-Struct werden als Referenz weitergegeben) führen und würde Perfomance-Buße einbringen... mehr dazu hier
Religionskriege sind Konflikte zwischen erwachsenen Menschen, bei denen es darum geht, wer den cooleren, imaginaeren Freund hat
Hallo m0rius,
gibt es dagegen etwas einzuwenden?
kann man schon so machen.
Ich frage mich allerdings, warum du bei HSV double, aber bei RGB ints benutzt. Ich würde in beiden Fällen für double plädieren, denn für RGB mit ints gibt es ja schon die Color-Struktur aus dem Framework.
Außerdem frage ich mich, warum du die Konvertierung asymmetrisch angelegt hast. Nach Color gibt es einen impliziten Cast wogegen es von Color nur die statischen Methoden von ColorConvert gibt.
herbivore
Hallo,
wenn die Performance die einzige Gefahr bzw. der einzige Nachteil an der Interface-Implementierung ist, dann stellt sie für mich kein signifikantes Problem dar.
Die Alternative wäre die Verwendung einer Klasse, was aber design-technisch nicht ganz sauber ist. Oder würde dieser Aufwand i.A. die Verwendung einer Klasse legitimieren, obwohl eine Farbe keine Identität besitzt?
Ich habe bei RGB Ganzzahlen verwendet, da im RGB-Farbraum Farben durch ganzzahlige Farbanteile repräsentiert werden, die Eigenschaften im HSV-Raum jedoch durch Gleitkommazahlen im Intervall [0,1].
Warum, herbivore, würdest du für double
-Eigenschaften plädieren?
Die implizite Konvertierung nach Color
habe ich eingefügt, da die Strukturen so recht einfach zu verwenden sind, wenn sie einer Eigenschaft zugewiesen werden, die eine Color
-Struktur erwartet.
Damit die Strukturen nicht mit Konvertierungsmethoden überfüllt werden (Single Responsibility Principle), habe ich eine eigene Klasse für die Konvertierungszwecke erstellt. Mein Gedankengang war, dass ich auf diesem Weg beim Hinzufügen neuer Farbräume bzw. Darstellungsmöglichkeiten nicht die Farbklassen anfassen muss ...
m0rius
Mein Blog: blog.mariusschulz.com
Hochwertige Malerarbeiten in Magdeburg und Umgebung: M'Decor, Ihr Maler für Magdeburg
Würden in dem Fall die TypeConverter was bringen? Wenn man den registriert müsste man theoretisch mit
System.Convert.ChangeType(color, typeof(HsvColor));
konvertieren können. Ich hab allerdings praktisch keine Erfahrung damit.
edit: Methodensignatur fixed.
loop:
btst #6,$bfe001
bne.s loop
rts
Hallo m0rius,
Warum, herbivore, würdest du für
double
-Eigenschaften plädieren?
zum Beispiel um Rundungsfehler bei der (wiederholten) Konvertierung von RGB in HSV und zurück sowie überhaupt beim Rechnen mit Farbwerten zu verringern.
Mal abgesehen davon, dass es rein willkürlich ist, HSV durch Gleitkommazahlen im Intervall [0,1] zu definieren und es auch andere Definitionen gibt, bei denen HSV als Integer (0-359, 0-100, 0-100) repräsentiert werden.
Wobei man natürlich auch zwischen internem Typ und dem Typ der Properties unterschieden könnte.
Mein Gedankengang war, dass ich auf diesem Weg beim Hinzufügen neuer Farbräume bzw. Darstellungsmöglichkeiten nicht die Farbklassen anfassen muss ...
Ok, da hast du natürlich Recht. Um den Preis einer sehr prozeduralen Herangehensweise. Aber einen Tod muss man sterben.
herbivore
wenn die Performance die einzige Gefahr bzw. der einzige Nachteil an der Interface-Implementierung ist, dann stellt sie für mich kein signifikantes Problem dar.
Nein... die eigentliche Gefahr ensteht beim Boxing. Hast du dir den Link durchgelesen?
Damit die Strukturen nicht mit Konvertierungsmethoden überfüllt werden ( Single Responsibility Principle), habe ich eine eigene Klasse für die Konvertierungszwecke erstellt. Mein Gedankengang war, dass ich auf diesem Weg beim Hinzufügen neuer Farbräume bzw. Darstellungsmöglichkeiten nicht die Farbklassen anfassen muss ...
das mit dem SRP-Zeugs ist zwar richtig, (Cast-)Operatoren sollten da aber eine Ausnahme sein (Immerhin dienen sie ja zu erleichterung... nicht als direkte Methode).
Mein Vorschlag: lass das mit den Interfaces. Implementiert stattdessen in jeden Struct ein Operator um XColor -> System.Drawing.Color zu Casten und einen weiteren Operator um wieder zurück zu casten
Religionskriege sind Konflikte zwischen erwachsenen Menschen, bei denen es darum geht, wer den cooleren, imaginaeren Freund hat
Hallo pohlmann,
Hast du dir den Link durchgelesen?
auch wenn ich nicht m0rius bin, hab ich ihn (jetzt) gelesen. Also mich hat die Argumentation nicht überzeugt.
Es wird angekreidet, dass die Änderung an p in dem folgenden Code nicht auf employee durchschlägt.
IPromotion p = employee;
Console.WriteLine(employee);
p.promote();
Console.WriteLine(employee);
Und es wird gesagt, dass die Änderung durchschlagen würde, wenn Employee eine Klasse wäre. Aber wenn Employee absichtlich ein Werttyp ist, dann ist es nicht nur vollkommen korrekt sondern geradezu erwünscht, dass die Änderung nicht durchschlägt. Zum Vergleich folgender Code:
Employee e = employee;
Console.WriteLine(employee);
e.promote();
Console.WriteLine(employee);
Da schlägt die Änderung an e auch nicht auf employee durch und das ist ja auch gerade erwünscht. Die Bemerkung, dass die Änderung durchschlagen würde, wenn Employee eine Klasse wäre, wäre auch bei diesem Code zwar richtig, aber eben vollkommen irrelevant, wenn Employee absichtlich ein Werttyp ist.
Alles dreht sich also letztlich um die Frage, ob etwas konzeptionell ein Werttyp sein sollte oder nicht. Nur hat diese Frage nichts damit zu tun, ob ein Interface definiert wurde oder nicht.
Und bei der Aussage, dass Employee eine Klasse sein sollte, gebe ich dem Autor sogar Recht, weil Employee-Objekte ja eine Identität haben. Aber das liegt überhaupt nicht daran, dass ein Interface verwendet wurde.
herbivore
Hallo pohlmann,
ja, ich habe den Blogbeitrag gelesen. Allerdings hat mich das nicht überzeugt, die Strukturen aus diesem Grund in Klassen zumzuwandeln (s. Argumentation oben).
Hallo herbivore,
ich würde das Verhalten einer RgbColor
-Struktur nicht erwarten, die nach außen ganzzahlige Eigenschaften R
, G
& B
präsentiert, intern jedoch Fließkommazahlen speichert, sodass dadurch bei einer Konvertierung in den HSV-Farbraum möglicherweise ein anderes Ergebnis erzeugt wird, als es durch die ganzzahlige Variante geschehen würde!
Nun ja, ich finde die Herangehensweise einer Konvertierungsklasse auf diese Weise nicht so abwegig; ich habe mich da an die Convert
-Klasse aus dem Framework angelehnt (daher auch nicht ColorConverter
, wie ich es sonst genannt hätte).
Ich werde wahrscheinlich trotzdem die Konvertierungsklasse entfernen, da außer dem RGB- und dem HSV-Farbraum keine Unmengen an weiteren Farbräumen vorhanden sein werden.
Wie du sagst, einen Tod muss man sterben; allerdings wäre es bei der Verlagerung der Konvertierungsmethoden in die jeweilige Klasse sauberer, die Methoden dafür bereits im Interface IColor
vorzuschreiben, oder? Schließlich soll jede Farbe, die IColor
implementiert, in jede andere Darstellungsform gebracht werden können.
So, wie ich das auf der Grundlage eurer Argumentation verstehe, geht die ausschließliche Verwendung von Strukturen und den in ihnen enthaltenen Konvertierungsmethoden- und Operatoren den Boxingproblemen und OOP-Unreinheiten aus dem Weg, richtig?
m0rius
Mein Blog: blog.mariusschulz.com
Hochwertige Malerarbeiten in Magdeburg und Umgebung: M'Decor, Ihr Maler für Magdeburg
Und es wird gesagt, dass die Änderung durchschlagen würde, wenn Employee eine Klasse wäre. Aber wenn Employee absichtlich ein Werttyp ist, dann ist es nicht nur vollkommen korrekt sondern geradezu erwünscht, dass die Änderung nicht durchschlägt.
IPromotion employee = new Employee("Cool Guy", 65);
IPromotion employee2 = employee;
Console.WriteLine(employee2); // ausgabe: 65
employee.promote();
Console.WriteLine(employee2); // ausgabe: 66!!
Gz
Religionskriege sind Konflikte zwischen erwachsenen Menschen, bei denen es darum geht, wer den cooleren, imaginaeren Freund hat
Hallo pohlmann,
hier ist Employee
als Klasse implementiert, nicht als Struktur.
m0rius
Mein Blog: blog.mariusschulz.com
Hochwertige Malerarbeiten in Magdeburg und Umgebung: M'Decor, Ihr Maler für Magdeburg
Hallo m0rius,
ich würde das Verhalten einer
RgbColor
-Struktur nicht erwarten, ...
Es ist doch aber nichts ungewöhnliches, dass bei Datentypen die interne Genauigkeit größer ist.
ich habe mich da an die
Convert
-Klasse aus dem Framework angelehnt :::
In Anlehnung an das Framework müsste die dann aber eigentlich ConvertColor heißen, damit der "Gag", dass sich der Methodenaufruf wie ein korrekter Satz liest (Convert.ToByte ==> convert to byte), erhalten bleibt. Oder noch besser müssten deine Methoden als Erweiterungsmethoden von Convert definiert sein (kann man eigentlich Klassen auch um statische Methoden erweitern?).
allerdings wäre es bei der Verlagerung der Konvertierungsmethoden in die jeweilige Klasse sauberer, die Methoden dafür bereits im Interface IColor vorzuschreiben, oder?
Mein Vorschlag war ja über beide Richtungen Casts zu verwenden.
So, wie ich das auf der Grundlage eurer Argumentation verstehe, geht die ausschließliche Verwendung von Strukturen und den in ihnen enthaltenen Konvertierungsmethoden- und Operatoren den Boxingproblemen und OOP-Unreinheiten aus dem Weg, richtig?
Die ausschließliche Verwendung von Strukturen ist ok. Den Schluss kann ich allerdings nicht nachvollziehen. Bei der Verwendung des Interfaces würde es schon weiterhin zu Boxing kommen. In meiner Argumentation oben ging es ja nur darum, dass die Verwendung von Boxing durchaus zum gewünschten Ergebnis führt. Vielleicht meinst du das.
hier ist
Employee
als Klasse implementiert, nicht als Struktur.
Nö, der Effekt in dem Beispiel von pohlmann tritt unabhängig davon auf, ob Employee eine Klasse oder eine Struktur ist.
Hallo pohlmann,
auch dein neues Beispiel kann man als korrektes Verhalten ansehen. Nach der Zuweisung des Werts an employee hat man - durch das Boxing - eine Referenz auf ein Objekt vom (statischen) Typ IPromotion, denn Variablen vom Typ eines Interfaces sind immer Referenzvariablen. Ein Interface kann zwar sowohl von einigen Strukturen als auch von einigen Klassen implementiert sein, aber in dem Moment wo man die Zuweisung von employee an employee2 macht, arbeitet man ja bereits ausschließlich auf der Ebene des Interfaces. Welchen Typ das Objekt in employee hat, ist nicht mehr (zwangsläufig) bekannt, interessiert nicht mehr bzw. darf im Grunde nicht mehr interessieren. Man darf bzw. muss davon ausgehen, dass man mit einer Referenz arbeitet.
Das ist aber nun auch wieder nichts spezielles, was durch das Interface entsteht, sondern einfach eine Eigenschaft des Boxings. Wenn überhaupt müsste man also das Boxing und nicht die Verwendung von Interfaces in Frage stellen.
herbivore
Hallo herbivore,
ich habe mich mit "ausschließlich" zu ungenau ausgedrückt, gemeint war die Verwendung von Strukturen ohne die Implementierung von Interfaces.
m0rius
Mein Blog: blog.mariusschulz.com
Hochwertige Malerarbeiten in Magdeburg und Umgebung: M'Decor, Ihr Maler für Magdeburg
Hallo m0rius,
ja, dann solltest du auf jeden Fall auf der sicheren Seite sein.
herbivore