Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

Sacaldur

Community-Fossil

Beiträge: 2 301

Wohnort: Berlin

Beruf: FIAE

  • Private Nachricht senden

81

16.01.2014, 22:10

Ob das Framework schlecht umgesetzt ist: Man hätte es durchaus anders/sauberer machen können. (Ich habe es mir nicht genauer angesehen, aber wenn es unabhängig von der Klasse ist, auf die es zugreift, aber dennoch auf beliebige Properties zugreifen kann, muss wohl Reflection verwendet werden. Ich persönlich würde Reflection wirklich nur dann nutzen, wenn es keinen anderen Weg gibt.)
Das Problem an einem sauberen Design ist aber, dass Benutzer eines Frameworks es möglichst einfach haben wollen und müssen diese erst ihr eigenes Design überdenken, um ein Framework einsetzen zu können, wird das Framework lieber gar nichts erst verwendet.

Woher die Datenquelle ihre Daten bekommt? Per Parameter während der Erzeugung, per Datenbankabfrage, Per Benutzereingabe oder wie auch immer (es ist Implementierungssache, grundsätzlich würde ich Konstruktorparameter bevorzugen). Es ist durchaus möglich, dafür Properties (oder Getter und Setter) anzulegen, es ist aber nicht notwendig.

Meines Wissens ging es die ganze Zeit nur darum, ob/wann Getter und Setter eingesetzt/nicht eingesetzt werden sollten, nicht aber darum, ob sie in konkreten Fällen notwendig sind (bspw. weil Frameworks sie erfordern). An der Stelle möchte ich noch anmerken, dass man das "manchmal geht es nicht anders" auch falsch interpretieren kann (ich hatte dabei nicht an Voraussetzungen von Frameworks gedacht, die aber nichts mit "dem Ideal" zu tun haben).
Mit Getter und Setter sind in dem Fall alle Arten von Methoden gemeint, die lediglich lesenden bzw. schreibenden Zugriff auf ein am Objekt vorhandenes Feld gewährleisten. (Properties lassen sich zwar wie Membervariablen aufrufen, verhalten sich im Hintergrund aber wie Methoden, daher sind die jeweiligen get- und set-Teile wie Methoden zu behandeln.)
Wenn es also keine Prüfungen oder Umrechnungen gibt, sind diese ohnehin unsinnig und man könnte den Member öffentlich zugänglich machen, wodurch man sich auch die impliziten Methodenaufrufe sparen würde. (Gut Compiler erkennen solche Fälle natürlich, wodurch auch im Falle von Gettern und Settern der Compiler diese wegoptimieren würde.)
Auch wenn eine Prüfung, Umrechung oder weitere Aktionen in einem Getter oder Setter ausgeführt werden würden, müsste man sich überlegen, ob man wirklich einen "Getter" oder "Setter" dafür braucht. Wenn der jeweilige Wert den aktuellen Zustand (oder einen Teil davon) des Objekts darstellt und es nur eine begrenzte Anzahl gültiger Möglichkeiten gibt, dann sollte es evtl. eher Methoden geben, um den Zustandswechsel hervorzurufen. Statt ein IsVisible zu setzen bzw. setVisible aufzurufen, könnte es eher ein Show und Hide bzw. Close geben. Bei den letzteren ist einem völlig egal, ob nun intern ein Boolean oder ein Enumwert gesetzt wird, man muss die Enumeration außen ggf. gar nicht mal kennen.

Das Tweening wiederum würde nichts holen, sondern aufgerufen werden, dann einen Timer starten oder einen Thread starten oder was die Umgebung dafür passendes gerade bereit hält und per Callback der Datenquelle von dem neuen Zwischenwert des Übergangs berichten.
Falls du mit der Frage nach dem Interesse nach der Buttonfarbe zu jedem Zeitpunkt auf das Durchreichen der Datenquelle hinaus willst: Das ist grundsätzlich keine Unsinnige Hinterfragung. Es könnte durchaus der Gedanke kommen, dass man nicht von der Datenquelle ein Callback angibt, sondern vom Button (oder gleich eine Property vom Button, wie bei dem Framework), allerdings ist der Sinn der Angabe einer Datenquelle, dass es eine abstrakte Beschreibung (interface) davon gibt, wie diese aussehen soll (bspw. event Action<Color> ColorChanged;) und es dazu verschiedene Implementierungen gibt (statische Farbe, Farbe mit linearem Übergang, pulsierende Farbe bei bestimmten Umständen, ...). Damit dieses einheitlich beibehalten wird, muss der indirekte Weg über die Datenquelle genommen werden.
Anhand von "Objektverwaltung" finde ich nichts, ich weiß also nicht, was du damit meinst.


Objekte Wegschmeißen:
Ich hatte bereits die Möglichkeit, vorzuschlagen, dass dem Button die Hintergrundfarbe im Konstruktor übergeben wird und jedes Mal, wenn die Farbe sich ändern soll, wird der alte Button weggeschmissen und ein neue erzeugt.
Das habe ich aber nicht vorgeschlagen, sondern eine Möglichkeit genannt, bei der man den gleichen Button behält, bei der man aber dennoch keine reinen Getter und Setter benötigt.


@3D_BYTE:
An der Stelle kann ich wohl auch schon eine Antwort geben:
Die Eigenschaften eines Objekts sollten von diesem Objekt verarbeitet werden.
Brush wäre bei ihm wohl lediglich eine Schnittstelle, wovon es mehrere Implementierungen geben kann. Die Implementierungen können dann bspw. eine einzige Farbe zeichnen (und erwarten dann eine Farbe), ein Bild zeichnen (und erwarten ein solches und Positionierungs-/Ausrichtungsangaben) oder Pfade Zeichnen (wofür diese erwartet werden würden).
In der Hinsicht kann man sich die verschiedenen Brushes aus WPF angucken, da es dort auch eine abstrakte Basisklasse und verschiedene Implementierungen gibt.
Im Falle von dot würden die von dem jeweiligen Brush erwarteten Eigenschaften bei der Erzeugung zugewiesen werden.
Die Vorteile, die ich darin sehe, sind, dass man weniger falsch machen kann, wenn man auf entsprechende Schnittstellen zugreift und dass es (je nach Auffassung) sauberer ist.

Und ich gehe einfach mal davon aus, dass die C# Variante der Properties hier bekannt sind. Ob man nun Getter/Setter oder Properties verwendet, ist für die aktuelle Diskussion (da das hier ja nicht mehr das Thema über die Programmiersprache ist) aber nicht wirklich von Bedeutung.
Spieleentwickler in Berlin? (Thema in diesem Forum)
---
Es ist ja keine Schande etwas falsch zu machen, als Programmierer tu ich das täglich, [...].

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »Sacaldur« (17.01.2014, 00:03)


3D BYTE

Frischling

Beiträge: 12

Wohnort: Neu-Isenburg (b. Frankfurt/main)

  • Private Nachricht senden

82

16.01.2014, 23:32

Zitat

Objekte Wegschmeißen:
Ich hatte bereits die Möglichkeit vorzuschlagen, dass dem Button die Hintergrundfarbe im Konstruktor übergeben wird und jedes Mal, wenn die Farbe sich ändern soll, wird der alte Button weggeschmissen und ein neue erzeugt.
Das habe ich aber nicht vorgeschlagen, sondern eine Möglichkeit genannt, bei der man den gleichen Button behält, bei der man aber dennoch keine reinen Getter und Setter benötigt

Du verwirrst mich gerade, wenn du schreibst, dass du ggf. einerseits vorschlägst, ein Objekt lieber neu zu erstellen als eine bestimmte Eigenschaft zu verändern (die eigentlich folgend von anderen Objekten vearbeitet werden). Und dann im nächsten Satz schreibst, vorgeschlagen zu haben, dass man den Zustand des Objekts ändert, ohne das der Zustand direkt einwirkt, der aber einwirkt.

Zeig mir, wie geht das :huh:
http://www.3dbyte.de

Sacaldur

Community-Fossil

Beiträge: 2 301

Wohnort: Berlin

Beruf: FIAE

  • Private Nachricht senden

83

17.01.2014, 00:46

@3D BYTE:
Da hast du meine Sätze nicht ganz richtig verstanden:
Im 1. habe ich geschrieben, ich hätte das wegschmeißen vorschlagen können, am Anfang des 2. schrieb ich, dass ich es aber nicht gemacht habe.
Kurz: ich habe das Wegschmeißen nicht vorgeschlagen.


@LetsGo:
Ich habe einfach mal Google bemüht und die Universal Tween Engine für Java gefunden. Was vielleicht sogar noch besser sein könnte, als separate Accessors zu implementieren, wäre es, die eigenen Klassen das entsprechende Interface implementieren zu lassen. Dadurch hat man zwar immernoch indirekt einen Zugriff auf die internen Variablen der Klasse, allerdings auf abstrahierte Variante. (es ist egal, ob es Koordinaten, die Lebensenergie oder Farbwerte sind, die da interpoliert werden sollen, es ist immer das gleiche Datenformat.) Und damit wären keine Getter oder Setter im bisherigen Sinne notwendig (was zumindest etwas ist), inneren Klassen sei Dank.

Die Datenquelle ist nichts weiter als die Quelle eines Datums. Ob sie es selbst speichert oder von einem Webservice abruft ist für die Schnittstelle zum Ziel irrelevant. Wenn man eine Art Tweening implementieren, dann würde diese (aus Sicht des Ziels) hinter der Datenquelle hängen. Wenn das Tweening-Objekt eine Aktualisierung bekannt macht, würde die Datenquelle den Wert nur durchreichen, da für diese nur die Grenzwerte (von wo wohin interpoliert werden soll) von Relevanz sind (um Interpolationen zu starten).


Rafactoring:
Außer man verwendet bspw. C# und muss den Member nur in eine Property umwandeln. Der Aufruf von anderen Stellen aus bleibt der gleiche, also ist das unproblematisch.
typisch OOP: hier scheiden sich in diesem Thema ja die Geister, allerdings nicht an dem Punkt, ob Member oder Getter/Setter, sondern ob überhaupt externer Zugriff gewährt werden sollte.
Spieleentwickler in Berlin? (Thema in diesem Forum)
---
Es ist ja keine Schande etwas falsch zu machen, als Programmierer tu ich das täglich, [...].

Sacaldur

Community-Fossil

Beiträge: 2 301

Wohnort: Berlin

Beruf: FIAE

  • Private Nachricht senden

84

17.01.2014, 13:01

Ja kann man tun, aber oftmals sind Klassen schon vorhanden. Und dass schreibe ich lieber einen Accessor, als eine neue Klasse zu erstellen. Zudem bekommt der Accessor das Objekt so oder so beim GetValues und SetValues übergeben, habe also keinen Vorteil.
Der Vorteil wäre, dass kein wahlfreier Zugriff auf das Objekt von theoretischerweise jedem anderne Objekt aus möglich ist.


Ok ich kann auf Getter und Setter für das Objekt selbst verzichten (weil ich direkten Zugriff habe), nicht aber auf die Getter und Setter des Accessors. Und dabei verletze ich dann auch noch OOP, weil dann der Accessor in der Klasse des eigentlichen Objekt liegt, was von der Logik her dem Accessor widerspricht. Der ist ja gerade dafür da auf schon vorhandene Klassen zuzugreifen.
Nein, er ist dafür da, dass eine andere Stelle, die die Daten benötigt, diese in einem über die Schnittstelle definierten Format (float[]) erhalten kann. Das heißt auch, dass der Ort der Implementierung egal ist, genauso wie, ob der Acessor im Hintergrund auf ein einzelnes Objekt oder auf eine Sammlung von Objekten zugreift. (Die Schnittstelle impliziert zwar, dass diese auf genau 1 Objekt ausgelegt ist, allerdings nur, damit eine Instanz des Accessors für mehrere Objekte verwendet werden kann...)
Wenn man es als besser erachtet, wenn nicht die Klasse selbst diese Schnittstelle implementiert (und man die Klasse selbst geschrieben hat), kann man auch eine innere Klasse verwenden.

Das Pattern Accessor/Mutator braucht Getter und Setter, so ist das eben [...]
Die Schnittstelle für den Accessor der Tweenengine braucht nicht zwingend Getter und Setter, da diese nicht teil der Schnittstelle sind. Eine mögliche Implementierung ohne habe ich bereits genannt.
Das Mutator Pattern (sofern ich dazu das richtige Dokument gefunden habe) beschreibt ebenfalls nicht, dass Getter und Setter erforderlich sind. Der Zweck des Mutators ist es, das Erzeugen von Unmengen von Objekten zu sparen, indem ein Objekt häufig angepasst wird. Als Beispiel wurde ein Stringbuffer angegeben, allerdings greift man nicht direkt auf dessen char[] zu, um seine Änderungen durchzuführen, sondern hat entsprechende Methoden, mit denen die Operationen durchgeführt werden können.
Außerdem habe ich eine Beschreibung zum Accessor Pattern gefunden, die mich eher an DAOs erinnert, welche nichts mit dem aktuellen Thema zu tun haben. (Und auch für DAOs ist nicht definiert, dass diese im Hintergrund auf Getter und Setter zugreifen müssen.

Wie bereits geschrieben: wenn ich von Gettern und Settern schreibe, meine ich solche Methoden (und Properties), die lediglich den Zugriff auf Member durchreichen.
Einfaches Beispiel für Klassen bei denen das nicht der Fall ist, sind alle mir bekannten Containertypen. Diese besitzen zwar Methoden für den Zugriff auf die Elemente, die sie verwalten, aber sie bieten keinen Zugriff auf ihre Member. Man kann sich von einer ArrayList nicht einfach das von ihr verwaltete Array geben lassen. Auch toArray liefert immer nur eine Kopie des intern verwalteten Arrays (oder zumindest vom belegten Teil), da andernsfalls eine ungewollte Manipulation von außen möglich wäre.
Es geht mir außerdem nicht um den entwickelten Code anderer, durch den man evtl. keine Wahl hat. Es geht mir um Code-Sauberkeit im Allgemeinen und wie man meines Erachtens nach ein saubereres Design erreichen kann.
Spieleentwickler in Berlin? (Thema in diesem Forum)
---
Es ist ja keine Schande etwas falsch zu machen, als Programmierer tu ich das täglich, [...].

dot

Supermoderator

Beiträge: 9 757

Wohnort: Graz

  • Private Nachricht senden

85

17.01.2014, 17:04

Die TweenEngine hat aber gar keine Ahnung davon.
Die weiß nur ich hab Attribut A und soll das in der Zeit X mit einem bestimmten Interpolations-verfahren, in einer bestimmten Zeit, einem Wert B zuweisen.

Dann braucht es maximal einen Setter, aber keinen Getter.

Klar wird die Spiellogik den Tween(z.B Alphafading/Positionsänderung/Farbe) anstoßen, aber um die Engine zu verwenden muss sich eben Getter und Setter bereitstellen, denn es gibt keine Alternative, außer die Funktionalität im Spieleobjekt selbst zu implementieren.

Es gibt im momentanen Design keine Alternative, da das Design, so wie es ist, ja von Grund auf darauf baut. Man wird also im Rahmen des gegebenen Designs keine Lösung finden können, die ohne Getter/Setter auskommt. Ich verspreche ja keine Wunderheilungen. Man müsste das Design wohl von Grund auf überarbeiten, um zu einer solchen Lösung zu kommen...

Anderes Bsp:
Ich hab ein Button, der erzeugt bei Click ein Event. Dem Event ist eine VerursacherObjekt zugeordnet (der Button.) Im Zuge der Eventbehandlung(Dispatcher) wird dem Event dynamisch ein ZielObjekt(set) zugeordnet, wenn es bisher noch nicht bekannt war(get ==null). Ein Event ist abstrakt, es kann auch durch etwas anderes entstanden sein bei dem schon ein Zielobjekt bekannt war. Angekommen am Evenhandler, der für eben dieses Event zuständig ist werden sowohl Zielobjekt als auch Verursacherobjekt benötigt (get). (Der Button(Verursacher) wird ausgeblendet und das Zielobjekt tut irgendwas).
Immer dann wenn Ein Objekt eine Kette von Handlern (Consumerchain)oder sonstwas durchläuft (die Hierarchie einer Anwendung nach oben geht) muss es evtl mit Informationen, die zuvor nicht bekannt waren, angereichert werden. Da die jeweilige Ebene aber nicht weiß, ob die Information schon vorher bekannt war muss ich eben schauen, ob das der Fall ist oder nicht. (Getter/Setter)

Ich bin mir nicht ganz sicher, ob ich das richtig verstanden hab, aber das klingt mir sehr danach, als ob wir es hier mit dem typischen Problem zu tun haben, dass in der Hitze des Moments versucht wird, dem Event zusätzlich zu seiner eigentlichen Aufgabe noch einen Teil der Aufgabe eines anderen Objektes aufzubinden, weil es gerade angenehm erscheint. Die eigentliche Aufgabe dieses Events, das du beschreibst, ist es wohl, eine bestimmte Aktion auf einem entfernten Objekt auszuführen!? Das Routing zum Zielobjekt ist dagegen nicht Aufgabe des Events. Es ist Aufgabe des Dispatch-Systems. Das "Verursacherobjekt" ist bei der Erzeugung des Events ja bekannt, sehr wahrscheinlich erzeugt es das Event selbst. Alle für die Ausführung der Aktion benötigten Daten können dem Event also bei seiner Erzeugung bereits übergeben werden, wir brauchen weder Setter noch Getter. Und dies sind die einzigen Daten die das Event benötigt. Und auf diese Daten muss zu keinem Zeitpunkt von außen zugegriffen werden. Alles was das Event braucht, ist eine Methode notify(target) die am Zielort dann mit dem Zielobjekt als Parameter aufgerufen wird und die jeweilige Aktion ausführt.

Vor allem wenn es um ein verteiltes System geht, wird es irgendein Protokoll zur Übertragung des Events benötigen. Das Zielobjekt ist etwas, mit dem dieses Protkoll sich zu befassen hat, es hat im Event nichts zu suchen. Sobald du das Zielobjekt als Property in das Event packst, muss nun plötzlich jedes Event immer ein Zielobjekt haben. Das Event ist nun auf einmal abhängig von einem Implementierungsdetail des zur Verteilung von Events zufällig gerade eben verwendeten Protokolls und das Dispatch-Systems abhängig von einer konkreten Art von Event. Beide Abhängigkeiten sind völlig unnötig und hindern mich, ohne dass es einen technischen oder logischen Grund dafür gäbe, daran, mit meinem Dispatch System etwas anderes als diese eine spezielle Art von Event zu dispatchen oder mein Event durch ein anderes System mit einem anderen Protokoll, z.B. eines das für lokale Events optimiert ist, zu verteilen.

Bei dieser "Kette von Handlern" stoßen wir dann an das nächste Problem: Das Event soll nun, während es durch verschiedene Layer läuft, mit zusätzlichen Informationen "angereichert" werden. Du sagst zwar nichts über die Natur dieser Informationen, aber du sagst, dass jeder Layer testen muss ob die Information aus anderen Layern bereits "bekannt war". Mit anderen Worten: Alle deine Layer sind nun plötzlich nichtmehr unabhängig, sondern durch dieses Event abhängig voneinander, da der Code in jedem Layer alle Informationen, die durch andere Layer hinzugefügt worden sein könnten, kennen und berücksichtigen muss. Wenn sich an einem Layer weiter unten etwas ändert, müssen alle Layer darüber ebenfalls angepasst werden, um den Code konsistent zu halten. Eine bessere Lösung wäre hier vermutlich z.B. der Decorator Pattern.

Der strikte Verzicht auf Properties hat uns somit zu einem Design frei von unnötigen Abhängigkeiten zwischen Event, Dispatch Protokoll und den einzelnen Handlern geführt. Unser System ist dadurch wesentlich einfacher (keine komplizierten if-Abfragen mehr, die in allen möglichen Teilen des Systems alle möglichen Situationen behandeln müssen, die aus dem speziellen Verhalten aller möglichen anderen Teile des System resultieren können), modularer (alle Komponenten sind nun unabhängig), flexibler (Komponenten sind unabhängig => Komponenten sind austauschbar und wiederverwendbar), weniger fehleranfällig (kein Code mehr, der manuell konsistent gehalten muss), leichter lesbar (weil einfacher), leichter wartbar (weil einfacher, weniger fehleranfällig und leichter lesbar), besser erweiterbar (weil modular und flexibel) usw. geworden...


Sorry das ich zwischenfunke, aber das Property-System wie bei C# hat definitiv sehr viele Vorzüge und spart viel Schreibarbeit.

Es spart viel Schreibarbeit, die man besser von vornherein gar nicht hätte... ;)

Es ist in der Anwendung das Selbe in Grün, weil es wie Get- und Set-Methoden die gleichen Aufgaben hat. Und um ehrlich zu sein, geht es mir gewaltig auf den Nerv, in C++ jedes Get und Set ausführlich in den Interfaces zu deklarieren. Ob das jetzt einzelne Werte sind oder ganze Strukturen/Objekte ist vom Endeffekt egal. Vom Zugriff ist es schöner, einfacher, genauso sicher und lässt mich schneller gleiche sich wiederholende Ergebnisse erzielen. Von daher spricht viel für das System von C#.

Das Problem ist nicht das Schreiben der Get- und Set-Methoden, das eigentliche Problem ist, dass du sie überhaupt erst haben willst.

dot, weil ich dir beim Besten willen nicht mehr bei den ständigen Themenwechsel folgen kann, kannst du dich mal in einem konkreten Snippet mal ausdrücken, wie du auf Objekteigenschaften eingehst und veränderst?

Ich verwende dazu Methoden. Da ich auf klares und simples Design setze, habe ich praktisch ausschließlich den Fall, dass Code mit Objekten arbeitet und nicht mit deren Eigenschaften. Denn Code, der von Objekteigenschaften abhängig ist, ist in der Regel Code, der in eine Methode gehört...

Daten sind letztendlich auch Eigenschaften, deren Zugriff erforderlich ist (egal ob von der Schnittstelle oder der Implementation).

Wenn du in einer Klasse Daten hast, auf die von außen zugegriffen werden muss, dann ist das nichts Anderes als ein Zeichen dafür, dass diese Daten nichts in dieser Klasse verloren haben. Sehr oft liegt der Grund darin, dass man versucht, mehr als eine Responsibility in eine Klasse zu stopfen (wie z.B. in LetsGos Beispiel mit dem Event). Dazu sei gesagt, dass ich hier rein von Klassen im Sinne von OOP spreche, Klassen als Sprachmittel an sich kommen natürlich auch in nicht-OOP Kontexten zum Einsatz, wo man dann allerdings in der Regel die entsprechenden Member einfach public machen würde.

Weil die Nummer mit dem Brush und das jede Machart und Textur eine eigene Klasse besitzen soll, kaufe ich nicht ab.

Was genau "kaufst du mir daran nicht ab"?

Würdest du auch in konkreten Stichpunkten eingehen, wie es den Arbeitsprozess einfacher macht (Logik, Sicherheit, Performance) als bekannte Verfahren der Set und Get von Properties/Methoden?

Siehe z.B. meine Ausführungen zum Beispiel von LetsGo oben.

Aber gehen wir halt das Beispiel mit den Shapes und Brushes auch noch durch:

Um beispielsweise ein blaues Rechteck zu zeichnen, hätten wir eine Schleife, die alle vom Rechteck verdeckten Pixel abläuft und diese blau anmalt.
Um ein pinkes Rechteck zu zeichnen, hätten wir eine Schleife, die alle vom Rechteck verdeckten Pixel abläuft und diese pink anmalt.
Um ein schwarz-weiß-kariertes Rechteck zu zeichnen, hätten wir eine Schleife, die alle vom Rechteck verdeckten Pixel abläuft und diese abwechselnd schwarz oder weiß anmalt.
Um ein Rechteck mit Farbverlauf zu zeichnen, hätten wir eine Schleife, die alle vom Rechteck verdeckten Pixel abläuft und diese entsprechend dem Farbverlauf anmalt.
Um ein Rechteck mit einem Portrait von Queen Elizabeth II drauf zu zeichnen, hätten wir eine Schleife, die alle vom Rechteck verdeckten Pixel abläuft und diese entsprechend einem Portrait von Queen Elizabeth II anmalt.

Um einen blauen Kreis zu zeichnen, hätten wir eine Schleife, die alle vom Kreis verdeckten Pixel abläuft und diese blau anmalt.
Um einen pinken Kreis zu zeichnen, hätten wir eine Schleife, die alle vom Kreis verdeckten Pixel abläuft und diese pink anmalt.
Um einen schwarz-weiß-karierten Kreis zu zeichnen, hätten wir eine Schleife, die alle vom Kreis verdeckten Pixel abläuft und diese abwechselnd schwarz oder weiß anmalt.
Um einen Kreis mit Farbverlauf zu zeichnen, hätten wir eine Schleife, die alle vom Kreis verdeckten Pixel abläuft und diese entsprechend dem Farbverlauf anmalt.
Um einen Kreis mit einem Portrait von Queen Elizabeth II drauf zu zeichnen, hätten wir eine Schleife, die alle vom Kreis verdeckten Pixel abläuft und diese entsprechend einem Portrait von Queen Elizabeth II anmalt.

...

Fällt uns was auf?

Beim Zeichnen einer Form, kann man zwei fundamentale Vorgänge unterscheiden:
  • Bestimmen, welche Pixel angemalt werden sollen und
  • bestimmen, in welcher Farbe sie angemalt werden sollen.
Ich habe ja bereits auf abstrakter Ebene argumentiert, dass Farbe keine Eigenschaft einer Form ist. Wie man sieht, spiegelt sich dies hier auch auf Implementierungsebene wider.

Sobald wir unserem Rechteck eine Farbe als Property geben, können wir nur noch einfarbige Rechtecke malen. Wenn wir ihm einen Farbverlauf als Property geben, können wir keine einfarbigen Rechtecke mehr zeichnen, sondern nur noch welche mit Farbverlauf etc. Wir könnten natürlich separate Klassen für einfarbige Rechtecke und Rechtecke mit Farbverlauf und Rechtecke mit Textur etc. machen. Das wäre aber extrem viel Codeduplication, denn bis auf das Bestimmen der Pixelfarbe wäre der Code für alle gleich. Wir könnten ihm alle möglichen Properties geben und zur Laufzeit entscheiden, welche nun genommen werden soll. Das wäre aber ineffizient und auch fehleranfällig, da das Interface widersprüchliche Konfigurationen zuließe...

Daher separieren wir diese zwei unabhängigen Aufgaben in unabhängige Objekte:
  • Ein Shape repräsentiert eine geometrische Form, er bestimmt, welche Pixel angemalt werden sollen;
  • ein Brush repräsentiert den virtuellen Pinsel, mit dem ein Shape gemalt werden soll, er bestimmt, wie die Pixel angemalt werden sollen...
Shapes und Brushes können unabhängig voneinander kombiniert werden. Keine Codeduplication, alles sehr sauber gelöst, die Welt wurde wieder ein Stück besser... ;)

PS: Und nein, der Brush sollte keine Property eines Shape sein. Dadurch würde schon wieder ein Stück der soeben gewonnenen Flexibilität verloren gehen. Früher oder später wird man beispielsweise vielleicht auf die Idee kommen, dass es ab und zu doch ganz nett wäre, Shapes nicht nur anzumalen, sondern stattdessen ihren Umriss zu zeichnen, wobei wir uns mit einfachen, durchgezogenen Linien natürlich nicht zufrieden geben, sondern strichlierte und gepunktete und dicke und dünne und blaue und pinke und... Linien wollen. Dank unserem flexiblen Design kein Problem, brauchen wir nur das Konzept eines Pen einführen und unseren Shape damit malen...

Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von »dot« (17.01.2014, 17:43)


3D BYTE

Frischling

Beiträge: 12

Wohnort: Neu-Isenburg (b. Frankfurt/main)

  • Private Nachricht senden

86

17.01.2014, 21:23

Sorry das ich zwischenfunke, aber das Property-System wie bei C# hat definitiv sehr viele Vorzüge und spart viel Schreibarbeit.
Es spart viel Schreibarbeit, die man besser von vornherein gar nicht hätte... ;)


Gibt halt Dinge, die man braucht und notwendiges Übel sind. a) Das ist halt so, b) man kann sich die Arbeit aber auch einfacher machen.

Das Problem ist nicht das Schreiben der Get- und Set-Methoden, das eigentliche Problem ist, dass du sie überhaupt erst haben willst.
Ich kenne auch ehrlich gesagt keinen anderen Ansatz, der wirtschaftlich umsetzbar ist. Ich habe Get und Set nicht weil ich sie haben will sondern muss. Weil wie kommst du an Daten ran, die du brauchst und nicht bekommen kannst? Gar nicht.

dot, weil ich dir beim Besten willen nicht mehr bei den ständigen Themenwechsel folgen kann, kannst du dich mal in einem konkreten Snippet mal ausdrücken, wie du auf Objekteigenschaften eingehst und veränderst?
Ich verwende dazu Methoden. Da ich auf klares und simples Design setze, habe ich praktisch ausschließlich den Fall, dass Code mit Objekten arbeitet und nicht mit deren Eigenschaften. Denn Code, der von Objekteigenschaften abhängig ist, ist in der Regel Code, der in eine Methode gehört...


Ich verwende auch ein klares und simples Design. Es ist sogar bekannt und üblich. Na dann pack mir mal ein unsigned short in eine Methode. Und für Vektoren gibts da ein __m128 für das Kreuzprodukt; jeweils zwei Vektoren die reinkommen und einer der rauskommt.

Wenn du in einer Klasse Daten hast, auf die von außen zugegriffen werden muss, dann ist das nichts Anderes als ein Zeichen dafür, dass diese Daten nichts in dieser Klasse verloren haben. Sehr oft liegt der Grund darin, dass man versucht, mehr als eine Responsibility in eine Klasse zu stopfen (wie z.B. in LetsGos Beispiel mit dem Event). Dazu sei gesagt, dass ich hier rein von Klassen im Sinne von OOP spreche, Klassen als Sprachmittel an sich kommen natürlich auch in nicht-OOP Kontexten zum Einsatz, wo man dann allerdings in der Regel die entsprechenden Member einfach public machen würde.

Warum haben Daten nichts in meiner Klasse zu tun, deren Zustände für Außen für andere Objekte wichtig sind? Sorry, aber Probleme aka "Variablen" und "Funktionen/Methoden" in Objekte zu packen ist OOP. Wenn Klassen kommunizieren oder aufbauen ist das ein Bestandteil von OOP. Also ziehen wir die Schuhe heute mal richtigrum an und nicht anders.

Was genau "kaufst du mir daran nicht ab"?:

Gekürzt auf das Wesentliche...:
Um beispielsweise ein blaues Rechteck zu zeichnen, hätten wir eine Schleife, die alle vom Rechteck verdeckten Pixel abläuft und diese blau anmalt.
Um ein pinkes Rechteck zu zeichnen, hätten wir eine Schleife, die alle vom Rechteck verdeckten Pixel abläuft und diese pink anmalt.
Um ein schwarz-weiß-kariertes Rechteck zu zeichnen, hätten wir eine Schleife, die alle vom Rechteck verdeckten Pixel abläuft und diese abwechselnd schwarz oder weiß anmalt.
...

Um einen blauen Kreis zu zeichnen, hätten wir eine Schleife, die alle vom Kreis verdeckten Pixel abläuft und diese blau anmalt.
Um einen pinken Kreis zu zeichnen, hätten wir eine Schleife, die alle vom Kreis verdeckten Pixel abläuft und diese pink anmalt.
...

Fällt uns was auf?

...


Ja. Ich lese, dass du für jede Art von Rechteck eine Schleife beginnst. Wie hart verstößt das denn bitte gegen die Grundlagen der DIN ISO 9001:2008 zu Erweiterbarkeit, Portabilität, Wartbarkeit, ... und schlussendlich Performance. Ich sag dir was: Ich hab einen Get, das liefert mir ein Objekt als 4D Vector als float (x, y, z, w als public floats, oder als union für r, g, b, a, ... whatever). Und das alles ist Modding-Fähig und hat nur eine Schleife. Das man jetzt sagt, "Trennen wir die Farbe vom Objekt" ist... normal auch mit Get und Set verbunden. Schließlich muss dein Shape von den mitgelieferten Eigenschaften ja auch irgendwo Wind bekommen.

...
Sobald wir unserem Rechteck eine Farbe als Property geben, können wir nur noch einfarbige Rechtecke malen. Wenn wir ihm einen Farbverlauf als Property geben, können wir keine einfarbigen Rechtecke mehr zeichnen, sondern nur noch welche mit Farbverlauf etc. Wir könnten natürlich separate Klassen für einfarbige Rechtecke und Rechtecke mit Farbverlauf und Rechtecke mit Textur etc. machen. Das wäre aber extrem viel Codeduplication, denn bis auf das Bestimmen der Pixelfarbe wäre der Code für alle gleich. Wir könnten ihm alle möglichen Properties geben und zur Laufzeit entscheiden, welche nun genommen werden soll. Das wäre aber ineffizient und auch fehleranfällig, da das Interface widersprüchliche Konfigurationen zuließe...
...


Ja für sowas gibt es ja genau Interfaces, Implementierung und Verarbung. Mehrere Interfaces in einer Klasse zu kapseln ist dann natürlich nicht nur optimal, sondern auch sehr typisch. Mal nebenbei: Duftnote von modernem C++.
Jetzt die Quizfrage: Wie erfährt das eine Interface Shape von den Informationen von anderen beliebigen Interfaces Color, ohne ein Get (oder Set durch den Benutzer) aufzurufen?
Und virtuelle Funktionen sind so sicher wie alles andere (oder genauer: Funktionssprünge auf die Implementation). Nur weil eine konkrete Klasse beim Aufruf einen jmp weniger hat, heißt das nicht, dass man widersprüchliche Funktionen implementieren kann. Fällt dann entweder unter dem Kram "Fehler vor dem PC" oder wenn es bewusst war "Crack". Übrigens, wer sagt, dass eine Eigenschaft nicht auch ein Interface-Objekt sein kann, das verschiedenst implementiert sein kann...?

...
Daher separieren wir diese zwei unabhängigen Aufgaben in unabhängige Objekte:
  • Ein Shape repräsentiert eine geometrische Form, er bestimmt, welche Pixel angemalt werden sollen;
  • ein Brush repräsentiert den virtuellen Pinsel, mit dem ein Shape gemalt werden soll, er bestimmt, wie die Pixel angemalt werden sollen...
Shapes und Brushes können unabhängig voneinander kombiniert werden. Keine Codeduplication, alles sehr sauber gelöst, die Welt wurde wieder ein Stück besser... ;)
...


Ah OK jetzt doch der Wechsel zu multiplen Interfaces in einem Objekt. Das hat zwar überhaupt nichts mit Get und Set zu tun...?

Und jetzt wieder die Quizfragen:
Wie erfährt das eine Interface Shape von den Informationen vom anderen beliebigen Interface Color, ohne ein Get (oder Set durch den Benutzer) aufzurufen?
Wer sagt, dass eine Eigenschaft nicht auch ein Interface-Objekt sein kann, das verschiedenst implementiert sein kann...?
Und: Was hat das Feature "Interface" mit Feature "Auf Werte zugreifen" zu tun?


Ich bin da so frei zu dir ehrlich zu sagen:
- Schreibe weniger Inhalt pro Beitrag.
- Komm einfach mal auf den konkreten Punkt.
- Zieh mal einen roten Faden, was du eigentlich willst.
- Oder ob man daraus für die Praxis lernen könnte.
- Schreib in Englishen oder Deutschen Begriffen und gewöhn dir das verdammte Denglisch ab.
Denn so wie es sich von dir liest, stellst du einfachste Dinge
mal ganz locker Infrage ohne dir einen Kopf zu machen,
ob es überhaupt deine Infragestellung einen Sinn hat.

Quellcode sagt mehr als ein theoretisches Gedöns, was nicht praktikabel ist.

Ich hab die Zitate ohne das Tag benutzt und extra klein gemacht, damit man wenigstens noch den Überblick hat, worauf ich mich beziehe.
http://www.3dbyte.de

87

04.04.2014, 11:26

Wenn ich mal so frei sein darf.
Toller Wortwechsel ihr 3 :thumbsup:
macht richtig Spaß sich da durchzuwrummeln.

Aber im Ernst - 2 Jahre Threat für die Frage nach Getter-&Setteranwendung?^^
Mal ganz davon abgesehen, dass "gut oder schlecht"- Fragen meist solche platonischen Diskussionen herbeiführen, muss ich gestehen: Man kann eure Ansicht/direkte Meinung dazu, nicht mehr erkennen, so ab Seite 7/8. ?(

Gibt es also derweil ein Resümee :?:

DeKugelschieber

Community-Fossil

Beiträge: 2 641

Wohnort: Rheda-Wiedenbrück

Beruf: Software-Entwickler

  • Private Nachricht senden

88

04.04.2014, 16:16

Nimm meins: getter und setter sind unnötig wenn du den Wert sowieso einfach setzt, aka:

Quellcode

1
2
3
setter(wert){
    this.wert = wert;
}

Wenn sichergestellt werden soll das Werte valid sind ist es sinnvoll:

Quellcode

1
2
3
4
5
setter(wert){
    if(wert < 100){
        this.wert = wert;
    }
}

Ich nutze recht selten Setter, da ich entweder bei der Nutzung des Wertes prüfe, oder dies in die Doku lege. Wer dann Müll setzt und das Programm abstürzt hat nicht ordentlich gelesen.

Cookiezzz

Frischling

Beiträge: 91

Wohnort: Deutschland

Beruf: Schüler

  • Private Nachricht senden

89

04.04.2014, 17:09

Und was ist, wenn du erst den Wert einfach setzt und sich dann später rausstellt, dass eine Wertprüfung doch sinnvoll wäre? Dann kann man den kompletten Code umschreiben.
Man sieht, damit sind wir nicht weitergekommen und diese Diskussion wird noch weitere 10 Seiten andauern.
Ich denke ob man Getter/Setter jetzt verwendet ist Teil des persönlichen Stils. ;) Beides hat Vor- und Nachteile.

DeKugelschieber

Community-Fossil

Beiträge: 2 641

Wohnort: Rheda-Wiedenbrück

Beruf: Software-Entwickler

  • Private Nachricht senden

90

04.04.2014, 18:08

@Cookiezzz: stimmt hatte ich nicht bedacht ^^
Trotzdem werde ich sie vermeiden, der Nutzer der Schnittstelle sollte die Werte dann prüfen ob sie laut Doku sinn machen.

Werbeanzeige