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
Ohne Getter kannst Du aber auch die Elemente nicht in einem Editor zusammenstellen, denn dieser könnte die Werte ja nie wieder auslesen.Getter braucht man dann imo aber schon wieder nicht. Mein D3D GUI System beispielsweise, speichert überhaupt nur direkt das Layout der Glyphen in den UI Objekten und überhaupt keine semantische Information über den Text. Denn der Text muss ja von irgendwo herkommen, die semantische Information steckt also schon dort und gehört auf keinen Fall in die UI Objekte...
Unveränderlich nur bedingt, denn man muss dieses Maximum ja von Außen festlegen können, sonst kannst du nirgends verschiedene Gegner entwerfen.Maximale Lebenspunkte und Haarfarbe würde ich jetzt mal eher als Kandidaten für unveränderliche Attribute sehen.
Wenn das Modell sich selbst rendern kann, dann braucht es keinen Getter. Aber dann ist es automatisch ein ModelView, was auch nicht immer sonderlich cool ist. Wahlweise hat man dann eventuell ein Problem, dass Projektile oder andere Spieler/NPCs sich nie auf dieses Objekt hin bewegen könnten, weil sie die Position ja nicht abrufen können.Ja, da braucht man möglicherweise einen Setter, aber vermutlich keinen Getter. Oder umgekehrt.Zitat von »BlueCobold«
[...] die Position eines Objekts in der Welt, etc pp?
Wenn Du das hast, sprich einen ModelView, dann heißt das aber auch, dass Du das Modell nicht verändern kannst, während es sich rendert. Parallelität ausgeschlossen, weil Seiteneffekte es unmöglich machen würden.Hängt davon ab wie das Objekt gerendert wird. Aber in der Regel hast du Objekte, die sich selbst rendern, daher brauchst du auch hier wieder entweder nur einen Getter oder nur einen Setter, auf keinen Fall aber beide.
Generell stimme ich Dir schon zu, dot, aber wenn man anfängt die wildesten Spagate zu machen, weil man Setter und Getter umgehen will, dann wird das Ergebnis noch deutlich hässlicher, als wenn man sie einfach verwendet hätte.
Dieser Beitrag wurde bereits 7 mal editiert, zuletzt von »dot« (25.07.2012, 20:20)
Community-Fossil
Beruf: Teamleiter Mobile Applikationen & Senior Software Engineer
Ich sagte nicht, dass Visual und Logik vermanscht bei mir sind. Aber das logische Konstrukt muss den Text halten, daraus Grafik generieren und den Text auch nach außen wieder auslesbar anbieten. Ich sehe nicht, wie man das verhindern will. Auch in Deinem Beispiel bleibt mir unklar, wie die logische Schicht das bei dir anders machen will.Möglicherweise, möglicherweise auch nicht. Wie gesagt, lässt sich das eigentlich immer nur für die konkrete Anwendung beantworten. In meinem UI sind das logische und das graphische Modell getrennt, das Ganze ist strukturell ein klein bisschen von WPF inspiriert. Das logische Modell arbeitet mit dem graphischen Modell, letzteres enthält keinerlei semantische Information. Irgendwer in der logischen Schicht muss den Text aber setzen und ihn daher natürlich kennen. Es gibt absolut keinen Grund, den Text in den Visuals zu halten; man könnte sogar argumentieren, dass dadurch das Single Responsibility Prinzip verletzt würde...
Genau. Und statt nur maximalen Hitpoints im Konstruktor kommt dann noch die Haarfarbe, die Hose, die Jacke, die Schuhe, die Waffe, die Hautfarbe, Gesichtstyp und noch ca. zwei Dutzend andere Charakter-Eigenschaften dazu. Hübscher Konstruktor... Und wenn Du jetzt dem Player eine andere Jacke verpassen willst, musst Du den alten vernichten und einen neuen erzeugen? Vielleicht verstehe ich Dich auch einfach nur falsch, aber wie willst du den Usecase: "Als Spieler möchte ich eine Spielfigur steuern können, welche komplett customized werden kann." technisch gestalten. Wie würdest Du das lösen ohne Setter und Getter? Und wie willst Du das Teil dann rendern ohne dass das Modell die graphischen Repräsentations-Objekte erzeugt?Wieso? Man kann diese Werte über den Konstruktor angeben oder sonstwas. Es gibt auch für dieses Beispiel wieder keine allgemeine Antwort.
Dann nenn mir doch mal konkret wie Du das kapseln würdest, damit Du keine Setter oder Getter benötigen würdest. Und bitte nicht wieder mit "Im Konstruktor". So viele Parameter hat kein vernünftiger Konstruktor.Auch hier wieder: Man kann und sollte Graphik von Logik trennen, ich hab niemals gesagt, dass es ein und das selbe Objekt sein sollte oder gar muss.
Dann sehe ich nicht, wie man jemals einen Editor für Spiele bauen können will ohne Code-Smell. Irgendwelche Objekte müssen nun mal die Daten halten, die müssen rein und die müssen wieder raus. Da nur Container zu machen und dann Konstruktoren mit den Containern zu invoken klingt für mich nicht nach einem vernünftigen Design, auch wenn Du so ein Design gar nicht vorgeschlagen hast. Etwas anderes fällt mir da aber beim besten Willen nicht ein. Wollte man Objekte nicht konfigurieren können, dann bräuchte man vermutlich wirklich keine Getter oder Setter, das klingt für mich aber nach Hardcoding von Werten oder manuellem Hex-/XML-en von Config-Files. Sehr unbequem und WYSIWYG-unfreundlich.Nun, meine Erfahrung ist, dass es eigentlich nie "wilde Spagate" braucht. Es braucht nur ein fundamental anderes Design, das am Ende aber meist sehr viel flexibler und eleganter ist. Auch sag ich ja nicht, dass man absolut nie und nimmer Getter oder Setter verwenden darf. Man sollte aber auf jeden Fall versuchen, Getter und Setter zu vermeiden und spätestens ein Objekt, das Getter und Setter, also eine Property, die gelesen und geschrieben werden kann, hat, ist meiner Erfahrung nach praktisch immer als Code Smell zu werten...
Ich sagte nicht, dass Visual und Logik vermanscht bei mir sind. Aber das logische Konstrukt muss den Text halten, daraus Grafik generieren und den Text auch nach außen wieder auslesbar anbieten. Ich sehe nicht, wie man das verhindern will. Auch in Deinem Beispiel bleibt mir unklar, wie die logische Schicht das bei dir anders machen will.
Genau. Und statt nur maximalen Hitpoints im Konstruktor kommt dann noch die Haarfarbe, die Hose, die Jacke, die Schuhe, die Waffe, die Hautfarbe, Gesichtstyp und noch ca. zwei Dutzend andere Charakter-Eigenschaften dazu. Hübscher Konstruktor...Wieso? Man kann diese Werte über den Konstruktor angeben oder sonstwas. Es gibt auch für dieses Beispiel wieder keine allgemeine Antwort.
Und wenn Du jetzt dem Player eine andere Jacke verpassen willst, musst Du den alten vernichten und einen neuen erzeugen?
Und wie willst Du das Teil dann rendern ohne dass das Modell die graphischen Repräsentations-Objekte erzeugt?
Dann nenn mir doch mal konkret wie Du das kapseln würdest, damit Du keine Setter oder Getter benötigen würdest. Und bitte nicht wieder mit "Im Konstruktor". So viele Parameter hat kein vernünftiger Konstruktor.Auch hier wieder: Man kann und sollte Graphik von Logik trennen, ich hab niemals gesagt, dass es ein und das selbe Objekt sein sollte oder gar muss.
Irgendwelche Objekte müssen nun mal die Daten halten, die müssen rein und die müssen wieder raus.
Dieser Beitrag wurde bereits 6 mal editiert, zuletzt von »dot« (26.07.2012, 01:07)
Community-Fossil
Beruf: Teamleiter Mobile Applikationen & Senior Software Engineer
Dieser Beitrag wurde bereits 8 mal editiert, zuletzt von »BlueCobold« (26.07.2012, 07:17)
Draw thyself
One ramification of full field encapsulation is in user interface (UI) construction. If you can't use accessors, you can't have a UI builder class call a getAttribute() method. Instead, classes have elements like drawYourself(...) methods.
A getIdentity() method can also work, of course, provided it returns an object that implements the Identity interface. This interface must include a drawYourself() (or give-me-a-JComponent-that-represents-your-identity) method. Though getIdentity starts with "get," it's not an accessor because it doesn't just return a field. It returns a complex object that has reasonable behavior. Even when I have an Identity object, I still have no idea how an identity is represented internally.
Of course, a drawYourself() strategy means that I (gasp!) put UI code into the business logic. Consider what happens when the UI's requirements change. Let's say I want to represent the attribute in a completely different way. Today an "identity" is a name; tomorrow it's a name and ID number; the day after that it's a name, ID number, and picture. I limit the scope of these changes to one place in the code. If I have a give-me-a-JComponent-that-represents-your-identity class, then I've isolated the way identities are represented from the rest of the system.
Bear in mind that I haven't actually put any UI code into the business logic. I've written the UI layer in terms of AWT (Abstract Window Toolkit) or Swing, which are both abstraction layers. The actual UI code is in the AWT/Swing implementation. That's the whole point of an abstraction layer—to isolate your business logic from a subsystem's mechanics. I can easily port to another graphical environment without changing the code, so the only problem is a little clutter. You can easily eliminate this clutter by moving all the UI code into an inner class (or by using the Façade design pattern).
When is an accessor okay?
First, as I discussed earlier, it's okay for a method to return an object in terms of an interface that the object implements because that interface isolates you from changes to the implementing class. This sort of method (that returns an interface reference) is not really a "getter" in the sense of a method that just provides access to a field. If you change the provider's internal implementation, you just change the returned object's definition to accommodate the changes. You still protect the external code that uses the object through its interface.
Next, I think of all OO systems as having a procedural boundary layer. The vast majority of OO programs runs on procedural operating systems and talks to procedural databases. The interfaces to these external procedural subsystems are generic by nature. Java Database Connectivity (JDBC) designers don't have a clue about what you'll do with the database, so the class design must be unfocused and highly flexible. Normally, unnecessary flexibility is bad, but in these boundary APIs, the extra flexibility is unavoidable. These boundary-layer classes are loaded with accessor methods simply because the designers have no choice.
In fact, this not-knowing-how-it-will-be-used problem infuses all Java packages. It's difficult to eliminate all the accessors if you can't predict how you will use the class's objects. Given this constraint, Java's designers did a good job hiding as much implementation as they could. This is not to say that the design decisions that went into JDBC and its ilk apply to your code. They don't. We do know how we will use the classes, so you don't have to waste time building unnecessary flexibility.
Es ist ja keine Schande etwas falsch zu machen, als Programmierer tu ich das täglich, [...].
Community-Fossil
Beruf: Teamleiter Mobile Applikationen & Senior Software Engineer
Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von »dot« (16.01.2014, 18:56)
Ich persönlich würde ja von Properties eher Abstand nehmen...
Werbeanzeige