![]() |
Objekt Kommunikation
Hallo Delphianer,
Eigentlich dachte ich relativ fit zu sein in delphi, scheitere aber nun an einer trivialen Sache ... Wie kann ich elegant und effizient Informationen zwischen zwei Objekten austauschen, ohne daß diese sich kennen. Jedes Objekt hat eine eigene Unit. Sie sollen sich NICHT gegenseitig über uses einbinden Zitat:
|
Re: Objekt Kommunikation
Ein Event ist die Lösung. z.B.
Delphi-Quellcode:
und in UnitGUI:
type
TObjectData = class private FNotify : TNotifyEvent; public property OnNotify : TNotifyEvent read FNotify write FNotify; procedure NotifyGUI; end; implementation procedure TObjectData.NotifyGUI; begin if Assigned (FNotify) then FNotify (Self); end;
Delphi-Quellcode:
type TObjectGUI = class private Data : TObjectData; procedure OnNotify (Sender : TObject); public Constructor Create; end; implementation constructor TObjectGUI.Create; begin Data := TObjectData.Create; Data.OnNotify := OnNotify; end; procedure OnNotify (Sender : TObject); begin { Wird aufgerufen wenn in TObjectData NotifyGUI durchlaufen wird. } end; |
Re: Objekt Kommunikation
Zitat:
Um ein Oberflächenobjekt von einem Datenobjekt zu benachrichtigen, dass sich am Objektzustand etwas geändert hat, kann man in Delphi mit Eventhandlern arbeiten. Das Datenobjekt würde dazu einen Eventhandler bereitstellen, den es aufruft wenn es eine Nachricht weitergeben möchte. |
Re: Objekt Kommunikation
Okay wie wäre es mit einem bestimmten Vorfahrentyp? Du kannst doch eine abstrakte Klasse in UnitData erstellen, die z.B. UpdateFunktionen vorschreibt. Das TObjectGUI muss nun von dieser Klasse abgeleitet sein und überschreibt die abstrakte Methode. Dann hat die Unit UnitData nichts mit dem TObjectGUI zu tun, aber kann trozdem davon ausgehen, dass es die Funktion UpdateData gibt. :roll:
Delphi-Quellcode:
type
TObjectDataGUI = class public procedure UpdateData(...); virtual; abstract; end; TObjectData = class private FGUI : TObjectDataGUI; public property GUI : TObjectDataGUI read FGUI; ... end;
Delphi-Quellcode:
type
TObjectGUI = class(TObjectDataGUI) public procedure UpdateData(...); override; end; |
Re: Objekt Kommunikation
Für sowas gibt es Interfaces.
Du definierst irgendwo ein Interface, welches die Methoden zur kommunikation definiert. Diese Implementierst Du in Deiner Klasse. Die andere Klasse kann dann eine instanz dieser Klasse über das Interface benutzen, ohne die konkrete Implementierung (also die Unit) zu kennen. Beide müssen halt die Unit referenzieren, in der das interface daklariert ist. |
Re: Objekt Kommunikation
Zitat:
|
Re: Objekt Kommunikation
Hallo Delphianer,
Das Problem war komplizierter als erwartet. Eure Vorschläge hatte ich im Prinzip schon teilweise ausprobiert, war dabei aber auf die Nase gefallen. Es hatte nicht funktioniert, was eigentlich hätte gehen sollen. Ich hatte aber etwas wesentliches Unterschlagen ... Ein DataObject soll nämlich auch mehr als ein GUI-Elemente "benachrichtigen". Hier hatte ich den Fehler gemacht, im DataObject pro "Entfernter ObjectMethode" einen 4Byte Zeiger in einer Liste zu speichern. Das geht so natürlich nicht, da Object-Methoden versteckte DoppelZeiger sind. Folgende Lösung scheint zu funktionieren.
Delphi-Quellcode:
...
uData.pas
type TOccupiedDataEvent = procedure (Sender: TObject; aODE:integer; const aMessage: string) of Object; type TOccupiedData = Class(TObject) ... protected FOccupiedGrids : TStringList; FOccupiedDataEvent: TOccupiedDataEvent; procedure OccupiedDataEventSend(aODE:integer; const aMessage: string); ... public function OccupiedDataEventConnect(aODE:TOccupiedDataEvent):integer; function OccupiedDataEventDisconnect(aODE:TOccupiedDataEvent):integer; function TOccupiedData.OccupiedDataEventConnect { >>>>>>>>>>>>>>>>>>>>>>>>>>>> } (aODE:TOccupiedDataEvent):integer; var i1:integer; p2:^TOccupiedDataEvent; p3:^TMethod; begin // local init Result := 0; i1 := 0; p2 := Nil; p3 := Nil; // local main if Assigned(aODE) then begin // Suchen ob GUI-Element bereits verlinkt ist For i1 := 0 to FOccupiedGrids.Count-1 do begin p3 := Pointer(FOccupiedGrids.Objects[i1]); if (p3<>NIL) then begin if (p3^.Code = TMethod(aODE).Code) and (p3^.Data = TMethod(aODE).Data) then begin // bereits vorhanden p2 := @aODE; break; end; end; end; if (p2=Nil) then begin New(p3); p3^ := TMethod(aODE); FOccupiedGrids.AddObject('S',TObject(p3)); aODE(Self, odeConnected, 'Connected'); end; end; end; { <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< } function TOccupiedData.OccupiedDataEventDisconnect { >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> } (aODE:TOccupiedDataEvent):integer; var i1:integer; p2:^TOccupiedDataEvent; p3:^TMethod; begin // local init Result := 0; i1 := 0; p2 := Nil; p3 := Nil; // local main if Assigned(aODE) then begin // Suchen ob GUI-Element bereits verlinkt ist For i1 := FOccupiedGrids.Count-1 downto 0 do begin p3 := Pointer(FOccupiedGrids.Objects[i1]); if (p3<>NIL) then begin if (p3^.Code = TMethod(aODE).Code) and (p3^.Data = TMethod(aODE).Data) then begin // gefunden! // ... aus liste entfernen FOccupiedGrids.Delete(i1); // ... dynamischen speicher wieder freigeben Dispose(p3); p3 := NIL; // notification an ODE senden aODE(Self, odeDisConnected, 'DisConnected'); break; end; end; end; end; end; { <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< } procedure TOccupiedData.OccupiedDataEventSend { >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> } (aODE:integer; const aMessage: string); var i1:integer; p2:^TOccupiedDataEvent; p3:^TMethod; begin // local init i1 := 0; p2 := Nil; p3 := Nil; // local main // Entfernte ObjectMethode aufrufen For i1 := 0 to FOccupiedGrids.Count-1 do begin p3 := Pointer(FOccupiedGrids.Objects[i1]); if (p3<>NIL) then begin Pointer(p2) := p3; p2^(Self, aODE,aMessage); end; end; end; { <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< }
Delphi-Quellcode:
uGui.pas
... DataObject.OccupiedDataEventConnect(OccupiedDataEvent); ... procedure TOccupiedGrid.OccupiedDataEvent { >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> } (Sender: TObject; aODE:integer; const aMessage: string); begin CASE aODE of odeDataLoaded :begin if Assigned(FGrid) then begin FGrid.Invalidate; end; end; END; end; { <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< } |
Re: Objekt Kommunikation
Hallo Leute,
in diesem Threat wird gefragt und beantwortet, womit ich mich gerade herumschlage. Aber leider will es mir nicht gelingen, das Projekt ans Laufen zu kriegen. Folgende Aufgabenstellung: Ich entwickle ein Spiel, in dem man (sehr vereinfacht ausgedrückt) mit Raumschiffen rumfliegen und auf andere Schiffe ballern können soll. Da ich das Spiel vollständig objektorientiert programmieren will, muss ich mich mit der Frage beschäftigen, was genau passiert, wenn eine Einheit_1 auf eine Einheit_2 schießt. Ich habe - hierfür relevant - folgende Klassenstruktur: TSpiel - TSpieler - TEinheit - TWaffe Die Waffe soll nichts von den Einheiten wissen. Sie weiß nur, dass sie eine Waffe ist und was sie braucht um loszuballern. Die schießende Einheit soll nichts von der anvisierten Einheit wissen. Sie weiß nur das sie eine Einheit (ein Schiff) ist, eine Waffe hat und diese in irgend eine Richtung hin abfeuern soll. Der Spieler ist derjenige, der das in die Wege leitet und das Spiel, soll über ein Event erfahren, dass eine Einheit_1 in Richtung einer Einheit_2 geschossen hat. Das Spiel soll ermitteln, ob Einheit_1 einen Treffer landen kann und wenn ja, dies mit einem Event der Einheiten 2 mitteilen. Synonym zur ersten Antwort ganz oben von fajac habe ich das folgendermaßen umgesetzt:
Delphi-Quellcode:
type
TSpiel = class private _Spieler : TSpieler; _sName : String; _pVirtuelleWaffe : TWaffe; procedure OnWaffeAbfeuernEvent (Sender : TObject; koAktuelleKoordinaten, koZielKoordinaten: TKoordinaten; iSchussEnergie:Integer); public constructor Create; implementation constructor TSpiel.Create; begin _pVirtuelleWaffe := TWaffe.Create('Waffe_1', TRUE, 0, 0, 100); _pVirtuelleWaffe.OnWaffeAbfeuernEvent := OnWaffeAbfeuernEvent; _Spieler := TSpieler.Create(iMENSCH, 0, 'Frank', clYellow); _sName := 'TestSpiel'; end; procedure TSpiel.OnWaffeAbfeuernEvent(Sender : TObject; koAktuelleKoordinaten, koZielKoordinaten: TKoordinaten; iSchussEnergie: Integer); begin // Wird aufgerufen wenn in TWaffe "Feuern" durchlaufen wird. ShowMessage('Jetzt hat das auch mein Spiel bemerkt. Es hat das "OnWaffeAbfeuernEvent" erhalten.'); end;
Delphi-Quellcode:
type
TWaffe = class private _sName : String; _bFunktionsbereit : Boolean; _iMasse : Integer; _iPreis : Integer; _iZustand : Integer; fWaffeAbfeuernEvent: TWaffeAbgefeuert_Event; public constructor Create(sNameDerWaffe: String; bWaffeFunktionsbereit: Boolean; iMasse: Integer; iNeuPreis: Integer; iProzentZustand: Integer); property OnWaffeAbfeuernEvent: TWaffeAbgefeuert_Event read fWaffeAbfeuernEvent write fWaffeAbfeuernEvent; procedure Feuern(koAktuelleKoordinaten, koZielKoordinaten: TKoordinaten; iSchussEnergie: Integer); virtual; implementation procedure TWaffe.Feuern(koAktuelleKoordinaten, koZielKoordinaten: TKoordinaten; iSchussEnergie: Integer); begin //ToDo -oWaffe -cCODE: Waffen.Feuern(Wohin: TKoordinaten; iEnergie: Integer) if Assigned(fWaffeAbfeuernEvent) then begin fWaffeAbfeuernEvent(self, koAktuelleKoordinaten, koZielKoordinaten, iSchussEnergie); end; ShowMessage('Jetzt fängt meine Waffe an, wie wild, in der Gegend herum zu ballern...!'); end; Wie der versierte Delphianer erkennt, möchte ich nach dem Druck auf den Feuerknopf zwei MessageBoxen sehen. In der ersten soll stehen, dass die Waffe wie wild rum ballert und in der zweiten soll stehen, dass das Spiel davon weiß!!
Delphi-Quellcode:
procedure TfrmStartGBH.FeuerButtonClick(Sender: TObject);
var slWaffenListe : TStringList; pWaffe : TWaffe; koStart, koZiel : TKoordinaten; pSpiel : TSpiel; pSpieler : TSpieler; pAktiveEinheit : TEinheit; begin try pSpiel := TSpiel.Create; slWaffenListe := TStringList.Create; pSpiel.GibSpieler(pSpieler); pAktiveEinheit := pSpieler.GibAktiveEinheit; pAktiveEinheit.GibWaffenListe(slWaffenListe); if (not Assigned(slWaffenListe)) or (slWaffenListe.Count = 0) then begin lLogger.LogFmt('TfrmStartGBH.Button1Click: slWaffenliste nicht angelegt oder leer.', [], llError); lLogger.LogFmt('TfrmStartGBH.Button1Click: Lege eine Test-Waffe an.', [], llWarning); pWaffe := TWaffe.Create('Test-Waffe', TRUE, 100, 99, 100); end else begin pWaffe := (slWaffenListe.Objects[0]) as TWaffe; end; koStart := TKoordinaten.Create(0, 0); koZiel := TKoordinaten.Create(1, 1); pWaffe.Feuern(koStart, koZiel, 500); except on e: Exception do begin lLogger.LogFmt(' Exception: %s.', [e.Message], llError); end; end; {*** TfrmStartGBH.FeuerButtonClick ***} end; Tatsächlich erhalte ich aber nur eine Messagebox, nämlich die Vollzugsmeldung meiner Waffe! Das Spiel kriegt davon offenbar nichts mit. Weiß jemand, warum nicht? Schon mal vielen Dank im Voraus. |
Re: Objekt Kommunikation
Delphi-Quellcode:
// Hast du an dieser Stelle mal im Debugger überprüft, welcher Zweig ausgeführt wird? // Falls nämlich der erste, so ist das Event der TWaffe-Instanz nicht gesetzt. if (not Assigned(slWaffenListe)) or (slWaffenListe.Count = 0) then begin lLogger.LogFmt('TfrmStartGBH.Button1Click: slWaffenliste nicht angelegt oder leer.', [], llError); lLogger.LogFmt('TfrmStartGBH.Button1Click: Lege eine Test-Waffe an.', [], llWarning); pWaffe := TWaffe.Create('Test-Waffe', TRUE, 100, 99, 100); end else begin pWaffe := (slWaffenListe.Objects[0]) as TWaffe; end; |
Re: Objekt Kommunikation
Es wird der erste Zweig ausgeführt, ja.
Um das Event zu setzten müsste ich WAS tun? |
Alle Zeitangaben in WEZ +1. Es ist jetzt 10:55 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 by Thomas Breitkreuz