Polymorphismus
Solange Sie getType()
oder etwas Ähnliches verwenden, verwenden Sie keinen Polymorphismus.
Ich verstehe das Gefühl, dass Sie wissen müssen, welchen Typ Sie haben. Aber jede Arbeit, die Sie machen möchten, während Sie wissen, dass dies wirklich in die Klasse gedrängt werden sollte. Dann sagst du einfach, wann es geht.
Der Prozedurcode erhält Informationen und trifft dann Entscheidungen. Objektorientierter Code weist Objekte an, Dinge zu tun.
- Alec Sharp
Dieses Prinzip heißt erzählen, nicht fragen . Wenn Sie dem folgen, können Sie Details wie Typ nicht verbreiten und eine Logik erstellen, die auf sie einwirkt. Dadurch wird eine Klasse auf den Kopf gestellt. Es ist besser, dieses Verhalten in der Klasse beizubehalten, damit es sich ändern kann, wenn sich die Klasse ändert.
Verkapselung
Sie können mir sagen, dass niemals andere Formen benötigt werden, aber ich glaube Ihnen nicht und Sie sollten es auch nicht.
Ein netter Effekt der folgenden Kapselung ist, dass es einfach ist, neue Typen hinzuzufügen, da sich ihre Details nicht auf den Code verteilen, in dem sie angezeigt werden, if
und auf die switch
Logik. Der Code für einen neuen Typ sollte sich alle an einer Stelle befinden.
Ein typunabhängiges Kollisionserkennungssystem
Lassen Sie mich Ihnen zeigen, wie ich ein Kollisionserkennungssystem entwerfen würde, das performant ist und mit jeder 2D-Form funktioniert, ohne sich um den Typ zu kümmern.
![Geben Sie hier die Bildbeschreibung ein](https://i.stack.imgur.com/MEdZI.png)
Angenommen, Sie sollten das zeichnen. Scheint einfach. Es sind alles Kreise. Es ist verlockend, eine Kreisklasse zu erstellen, die Kollisionen versteht. Das Problem ist, dass wir dadurch eine Denkrichtung durchlaufen, die auseinanderfällt, wenn wir 1000 Kreise benötigen.
Wir sollten nicht an Kreise denken. Wir sollten über Pixel nachdenken.
Was wäre, wenn ich Ihnen sagen würde, dass Sie denselben Code, mit dem Sie diese Typen zeichnen, erkennen können, wenn sie sich berühren oder auf welchen der Benutzer klickt?
![Geben Sie hier die Bildbeschreibung ein](https://i.stack.imgur.com/tIOJ5.png)
Hier habe ich jeden Kreis mit einer einzigartigen Farbe gezeichnet (wenn Ihre Augen gut genug sind, um den schwarzen Umriss zu sehen, ignorieren Sie das einfach). Dies bedeutet, dass jedes Pixel in diesem versteckten Bild dem entspricht, was es gezeichnet hat. Eine Hashmap kümmert sich gut darum. Auf diese Weise können Sie tatsächlich Polymorphismus betreiben.
Dieses Bild müssen Sie dem Benutzer nie zeigen. Sie erstellen es mit demselben Code, der den ersten gezeichnet hat. Nur mit verschiedenen Farben.
Wenn der Benutzer auf einen Kreis klickt, weiß ich genau, welcher Kreis, weil nur ein Kreis diese Farbe hat.
Wenn ich einen Kreis über einen anderen zeichne, kann ich schnell jedes Pixel lesen, das ich überschreiben möchte, indem ich sie in einem Satz ablege. Wenn ich mit den Sollwerten für jeden Kreis fertig bin, mit dem er kollidiert ist, muss ich jeden nur noch einmal anrufen, um ihn über die Kollision zu informieren.
Ein neuer Typ: Rechtecke
Dies wurde alles mit Kreisen gemacht, aber ich frage Sie: Würde es mit Rechtecken anders funktionieren?
In das Erkennungssystem ist kein Kreiswissen gelangt. Radius, Umfang oder Mittelpunkt sind egal. Es kümmert sich um Pixel und Farbe.
Der einzige Teil dieses Kollisionssystems, der in die einzelnen Formen gedrückt werden muss, ist eine einzigartige Farbe. Ansonsten können die Formen nur daran denken, ihre Formen zu zeichnen. Darin sind sie sowieso gut.
Wenn Sie jetzt die Kollisionslogik schreiben, ist es Ihnen egal, welchen Subtyp Sie haben. Sie sagen, es soll kollidieren und es sagt Ihnen, was es unter der Form gefunden hat, die es vorgibt zu zeichnen. Typ muss nicht bekannt sein. Das bedeutet, dass Sie so viele Untertypen hinzufügen können, wie Sie möchten, ohne den Code in anderen Klassen aktualisieren zu müssen.
Implementierungsoptionen
Wirklich, es muss keine eindeutige Farbe sein. Dies können tatsächliche Objektreferenzen sein und eine Indirektionsebene speichern. Aber diese sehen in dieser Antwort nicht so gut aus.
Dies ist nur ein Implementierungsbeispiel. Es gibt sicherlich andere. Dies sollte zeigen, dass das gesamte System umso besser funktioniert, je näher Sie diese Formuntertypen an ihrer einzigen Verantwortung festhalten. Es gibt wahrscheinlich schnellere und weniger speicherintensive Lösungen, aber wenn sie mich dazu zwingen, das Wissen über die Subtypen zu verbreiten, würde ich es ablehnen, sie auch bei Leistungssteigerungen zu verwenden. Ich würde sie nicht benutzen, wenn ich sie nicht eindeutig brauchte.
Doppelversand
Bisher habe ich den Doppelversand völlig ignoriert . Ich habe das getan, weil ich konnte. Solange es der Kollisionslogik egal ist, welche beiden Typen kollidierten, brauchen Sie sie nicht. Wenn Sie es nicht brauchen, verwenden Sie es nicht. Wenn Sie glauben, dass Sie es brauchen könnten, verschieben Sie den Umgang damit so lange wie möglich. Diese Haltung nennt man YAGNI .
Wenn Sie entscheiden, dass Sie wirklich verschiedene Arten von Kollisionen benötigen, fragen Sie sich selbst, ob n-Form-Subtypen wirklich n 2 Arten von Kollisionen benötigen . Bisher habe ich sehr hart gearbeitet, um das Hinzufügen eines weiteren Formuntertyps zu vereinfachen. Ich möchte es nicht mit einer Doppelversand-Implementierung verderben , die Kreise dazu zwingt, zu wissen, dass Quadrate existieren.
Wie viele Arten von Kollisionen gibt es überhaupt? Ein wenig Spekulieren (eine gefährliche Sache) erfindet elastische Kollisionen (federnd), unelastisch (klebrig), energisch (explodieren) und destruktiv (schädlich). Es könnte mehr geben, aber wenn dies weniger als n 2 ist, können wir unsere Kollisionen nicht überdenken.
Das heißt, wenn mein Torpedo etwas trifft, das Schaden akzeptiert, muss er nicht wissen, dass er ein Raumschiff trifft. Es muss nur sagen: "Ha ha! Du hast 5 Schadenspunkte genommen."
Dinge, die Schaden verursachen, senden Schadensmeldungen an Dinge, die Schadensmeldungen annehmen. Auf diese Weise können Sie neue Formen hinzufügen, ohne die anderen Formen über die neue Form zu informieren. Sie verbreiten sich nur über neue Arten von Kollisionen.
Das Raumschiff kann zum Torp zurückschicken: "Ha ha! Du hast 100 Schadenspunkte genommen." sowie "Du steckst jetzt an meinem Rumpf fest". Und der Torp kann zurückschicken "Nun, ich bin fertig damit, mich zu vergessen".
Zu keinem Zeitpunkt weiß einer genau, was jeder ist. Sie wissen nur, wie sie über eine Kollisionsschnittstelle miteinander sprechen können.
Mit dem doppelten Versand können Sie die Dinge genauer steuern, aber möchten Sie das wirklich ?
Wenn Sie dies tun, denken Sie bitte zumindest darüber nach, einen doppelten Versand durch Abstraktionen darüber durchzuführen, welche Arten von Kollisionen eine Form akzeptiert, und nicht über die tatsächliche Formimplementierung. Kollisionsverhalten können Sie auch als Abhängigkeit einfügen und an diese Abhängigkeit delegieren.
Performance
Leistung ist immer kritisch. Das heißt aber nicht, dass es immer ein Problem ist. Testleistung. Spekulieren Sie nicht nur. Alles andere im Namen der Leistung zu opfern, führt normalerweise sowieso nicht zu Perforationscode.