![]() |
Zirkuläre Referenzen
Hallo, vorab: ja, ich habe schon Google und die Hauseigene Suche benutzt, bin aber nicht fündig geworden.
Ich weiß, was Zirkuläre Unit Referenzen sind, auch, dass ich reichlich viel falsch gemacht habe, wenn diese von Nöten sind, nur steh ich gerade komplett auf dem Schlauch, wie ich diese umgehen kann bzw. so umstrukturiere, dass sie nicht mehr da sind. Folgendes: Ich habe eine Map.pas. Wie der Name schon sagt, liegt darin die Klasse TMap, die einen 2D Array of TBlock (deklariert in Blocks.pas) speichert. Daneben vegetiert noch eine Entity.pas mit dem Typ TPlayer, welcher auf die Map.pas zwecks Kollisionserkennung zugreift. Nun möchte ich einen neuen Blocktyp einführen: Blöcke mit denen man interagieren kann. Damit der Player dies tun kann, dachte ich an eine neue Block Klass TActionBlock, welche die procedure OnAction (oder so) besitzt. Als Parameter wäre da ja ein TPlayer angemessen, damit ich weiß, welcher Player denn nun agieren möchte. Gleichzeitig dachte ich, es wäre klug, dem Player immer den gerade nutzbaren Block zu speichern (logischerweiße vom Typ TActionBlock). Da es hier sehr deutlich zu einem Kreis kommt (TPlayer <--> TActionBlock), habe ich nur die Position des Blocks gespeichert, geht ja auch. Nur gibt es noch immer eine Referenzschleife, die ich nicht zu umgehen weiß. TPlayer -> TMap -> TActionBlock -> TPlayer. (Sollten Code Schnipsel erwünscht sein, würde ich diese bereitstellen) Vielen Dank im Vorraus. |
AW: Zirkuläre Referenzen
Zitat:
|
AW: Zirkuläre Referenzen
Mangels Quelltext möchte ich nur einmal ein Beispiel geben wie man so etwas behandeln kann.
Nehmen wir an du hast einen TPlayer, der Zugriff auf die TMap bekommt, damit er die benachbarten Blöcke kennt. Warum muss er die kennen? Um damit zu interagieren. Nun könnte man aber eine zusätzliche Klasse TPlayerActor schaffen, die dies übernimmt. Diese bekommt von einer übergeordneten Instanz (dem Spielfeld z.B.) gesagt, dass Player XY mit Block XY interagieren möchte. Der TPlayer könnte dies z.B. per Event dem Spielfeld mitteilen. Auf die Weise kennt das Spielfeld die Blöcke und die Player, die Player und Blöcke sich aber nicht gegenseitig. Wie gesagt, das ist nur ein Beispiel, gefallen würde mir die Struktur auch nicht. Aber es könnte so ähnlich sein wie bei dir und dir einen Denkanstoß geben hoffe ich. |
AW: Zirkuläre Referenzen
Hallo,
TActionBlock darf nichts von TPlayer wissen, eher umgekehrt. Ich weiß jetzt nicht, ob das mit dem Visitor-, oder Observer-Pattern lösbar ist. Lösbar ist es auch jeden Fall durch Nutzung von Interfaces, dann bleibt die "zugrunde liegende" Klasse außen vor. |
AW: Zirkuläre Referenzen
Hallo und danke für die schnellen und vielen Antworten.
@Uwe Raabe
Delphi-Quellcode:
Ist das in etwa, was Du wolltest? Mehr? Weniger?
--------Entity.pas--------
uses Map, ...; type TPlayer = class(TEntity) private procedure updateCollision(AMap: TMap; newX,newY: Single); override; public NearestBlock: TBlockVector; constructor Create(EntityPosition: TVector; SizeH,SizeV: Single); override; [...] end; --------Map.pas-------- uses Blocks, ...; type TBlocks = array of array of TBlock; TMap = class(TObject) private Structure: TBlocks; [...] public [...] property Grid[X,Y: Word]: TBlock read GetStructure; default; property Grid[APos: TBlockVector]: TBlock read GetStructure; default; end; --------Blocks.pas-------- uses ...; type TBlock = class(TObject) [...] end; TActionBlock = class(TBlock) public procedure OnAction(APlayer: TPlayer); end; @jaenicke Grundsätzlich danke. Das Dilemma über Events zu lösen ist durchaus eine Variante. Ich muss zugeben mit Events zu programmieren ist mir neu (klar, Komponentenevents, ich meine hier sebstverständlich eigene). Was ich sagen will: Gibt es da eine Art Tutorialempfehlung des Chefs? @hoika Der Idee bin ich auch sehr aufgeschlossen. Darf ich erfahren, was Visitor-/Observer-Patterns sind? Ich habe bis dato noch nie von Interfaces gehört (außer im Unit Aufbau). Eine erst Recherche ergab nur die Tatsache, dass diese existieren und Dinge können. Auch hier die eFrage: Gibt es da etwas, was Du empfehlen kannst? Vielen Dank im vorraus |
AW: Zirkuläre Referenzen
Entity.pas braucht
Delphi-Quellcode:
.
Map.TMap
Map.pas braucht
Delphi-Quellcode:
.
Block.TBlock
Block.pas braucht
Delphi-Quellcode:
.
Entity.TPlayer
Was dann den Kreis schließt. Hier in diesem Falle würde ich alles in eine Unit packen, dann gibt es auch kein Problem mit der Kreis. |
AW: Zirkuläre Referenzen
Hier ein möglicher Lösungsvorschlag zum Aufbrechen des Kreises, ganz objektorientiert und ohne Verwendung von Interfaces (was sicher auch eine valide Lösung wäre). Statt direkt auf eine TPlayer-Instanz zuzugreifen, wird hier eine abstrakte Player-Klasse eingeführt.
Delphi-Quellcode:
Die Maps-Unit ist hier nicht relevant, da sie nur indirekt an dem Kreis beteiligt war und nicht selber einen gebildet hat.
unit Blocks;
interface type TBlock = class(TObject) end; TAbstractPlayer = class abstract public procedure DoWithBlock(ABlock: TBlock); virtual; abstract; end; TActionBlock = class(TBlock) public procedure OnAction(APlayer: TAbstractPlayer); end; implementation procedure TActionBlock.OnAction(APlayer: TAbstractPlayer); begin APlayer.DoWithBlock(Self); end; end. Eigentlich würde man TPlayer direkt von TAbstractPlayer ableiten. Da aber TPlayer schon von TEntity abgeleitet wird, schalten wir eine neue Adapter-Klasse dazwischen.
Delphi-Quellcode:
Natürlich muss das noch an die tatsächlichen Gegebenheiten angepasst werden, aber das Prinzip sollte erkennbar sein.
unit Entity;
interface uses Map, Blocks; type TEntity = class end; type TPlayer = class(TEntity) private FPlayerAdapter: TAbstractPlayer; public constructor Create; destructor Destroy; override; procedure DoWithBlock(ABlock: TBlock); end; implementation type TPlayerAdapter = class(TAbstractPlayer) private FPlayer: TPlayer; public constructor Create(APlayer: TPlayer); procedure DoWithBlock(ABlock: TBlock); override; end; constructor TPlayerAdapter.Create(APlayer: TPlayer); begin inherited Create; FPlayer := APlayer; end; procedure TPlayerAdapter.DoWithBlock(ABlock: TBlock); begin FPlayer.DoWithBlock(ABlock); end; constructor TPlayer.Create; begin inherited Create; FPlayerAdapter := TPlayerAdapter.Create(Self); end; destructor TPlayer.Destroy; begin FPlayerAdapter.Free; inherited Destroy; end; procedure TPlayer.DoWithBlock(ABlock: TBlock); begin end; end. |
AW: Zirkuläre Referenzen
Wenn ich mich nicht verlesen habe, verursacht tActionblock den Ärger. Also dessen Definition in den Player verschieben und es sollte funktionieren.
Gruß K-H |
AW: Zirkuläre Referenzen
Hallo
@Uwe Raabe Vielen vielen Dank für die Lösung :) Als ich die gelesen habe, habe ich erst gedacht: Ne, ist nicht ganz wie ich es brauche, aber nach einigen Minuten hat sich die Erkenntnis eingestellt: Doch, genau so könnte es klappen (Bin derzeit viel beschäftigt mit Schule, deswegen nur könnte). Ein, zwei Fragen hab ich dann doch noch. 1. Was macht
Delphi-Quellcode:
mit der Klasse?
class abstract
2. Warum die Adapter-Klasse? Kann ich nicht theoretisch einfach
Delphi-Quellcode:
machen?
class(TEntity, TAbstractPlayer)
|
AW: Zirkuläre Referenzen
Zitat:
Zitat:
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 22:06 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz