AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Thema durchsuchen
Ansicht
Themen-Optionen

Objekt Kommunikation

Ein Thema von peschai · begonnen am 2. Jul 2009 · letzter Beitrag vom 19. Okt 2009
Antwort Antwort
Seite 1 von 2  1 2      
Benutzerbild von peschai
peschai

Registriert seit: 15. Feb 2004
Ort: Göppingen
270 Beiträge
 
Delphi XE5 Professional
 
#1

Objekt Kommunikation

  Alt 2. Jul 2009, 12:55
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:
"UnitData" beinhaltet ein tObjectData

"UnitGUI" beinhalt ein TObjectGUI
und darf auch UnitData kennen ..."uses UnitData"

So jetzt die Aufgabe: wie kann eine Instanz von TObjectData einm zugehörigen Instanz von TObjectGUI die Info zukommen lassen: "Daten geändert, aktualisiere dich neu".

Auchtung! ein "Uses UnitGUI" in UnitData möchte ich vermeiden. Nicht FRagen warum, sondern das als Rahmenbedingung bitte akzeptieren
Peter Schaible
  Mit Zitat antworten Zitat
fajac

Registriert seit: 1. Jul 2009
60 Beiträge
 
#2

Re: Objekt Kommunikation

  Alt 2. Jul 2009, 13:08
Ein Event ist die Lösung. z.B.

Delphi-Quellcode:
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;
und in UnitGUI:
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;
  Mit Zitat antworten Zitat
mjustin

Registriert seit: 14. Apr 2008
3.006 Beiträge
 
Delphi 2009 Professional
 
#3

Re: Objekt Kommunikation

  Alt 2. Jul 2009, 13:15
Zitat von peschai:
Auchtung! ein "Uses UnitGUI" in UnitData möchte ich vermeiden. Nicht FRagen warum, sondern das als Rahmenbedingung bitte akzeptieren
Diese Rahmenbedingung ist nicht ungewöhnliches.

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.
Michael Justin
habarisoft.com
  Mit Zitat antworten Zitat
Benutzerbild von Desmulator
Desmulator

Registriert seit: 3. Mai 2007
Ort: Bonn
169 Beiträge
 
#4

Re: Objekt Kommunikation

  Alt 2. Jul 2009, 15:23
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.

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;
Lars
There are 10 kinds of people in the world:
those who get binary, and those who don’t.
  Mit Zitat antworten Zitat
Benutzerbild von Phoenix
Phoenix
(Moderator)

Registriert seit: 25. Jun 2002
Ort: Hausach
7.641 Beiträge
 
#5

Re: Objekt Kommunikation

  Alt 2. Jul 2009, 15:27
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.
Sebastian Gingter
Phoenix - 不死鳥, Microsoft MVP, Rettungshundeführer
Über mich: Sebastian Gingter @ Thinktecture Mein Blog: https://gingter.org
  Mit Zitat antworten Zitat
mjustin

Registriert seit: 14. Apr 2008
3.006 Beiträge
 
Delphi 2009 Professional
 
#6

Re: Objekt Kommunikation

  Alt 2. Jul 2009, 16:41
Zitat von Desmulator:
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.
Warum sollte man einen gemeinsamen Vorfahrtyp einführen? Dadurch wird man festgelegt. Spätere Änderungen am Vorfahrtyp wirken sich kettenreaktionsartig auf alle Unterklassen in allen Zweigen aus, was nicht immer unproblematisch ist. Eine Trennung in eine API (z.B. mit Interfaces) und Implementierung ist leichter änderbar.
Michael Justin
habarisoft.com
  Mit Zitat antworten Zitat
Benutzerbild von peschai
peschai

Registriert seit: 15. Feb 2004
Ort: Göppingen
270 Beiträge
 
Delphi XE5 Professional
 
#7

Re: Objekt Kommunikation

  Alt 4. Jul 2009, 18:40
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; { <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< }
Peter Schaible
  Mit Zitat antworten Zitat
Benutzerbild von Glühwürmchen
Glühwürmchen

Registriert seit: 31. Okt 2003
Ort: Pfinztal
156 Beiträge
 
Delphi 2010 Professional
 
#8

Re: Objekt Kommunikation

  Alt 17. Okt 2009, 18:36
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.
Gruß Glühwürmchen
<><
  Mit Zitat antworten Zitat
fajac

Registriert seit: 1. Jul 2009
60 Beiträge
 
#9

Re: Objekt Kommunikation

  Alt 19. Okt 2009, 07:53
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;
  Mit Zitat antworten Zitat
Benutzerbild von Glühwürmchen
Glühwürmchen

Registriert seit: 31. Okt 2003
Ort: Pfinztal
156 Beiträge
 
Delphi 2010 Professional
 
#10

Re: Objekt Kommunikation

  Alt 19. Okt 2009, 09:04
Es wird der erste Zweig ausgeführt, ja.
  • Auszug aus dem Logfile
    19.10.2009 09:00:14 -0- ..TfrmStartGBH.FeuerButtonClick: ---START---
    19.10.2009 09:00:14 -0- ....TWaffe.Create: ---START---
    19.10.2009 09:00:14 -0- ....TWaffe.Create: ---ENDE---

    19.10.2009 09:00:14 -0- ....TSpieler.Create: ---START--- IN: _iSpielerTyp = 1; _iVermoegen = 0; _sName = Frank
    19.10.2009 09:00:14 -0- ....TSpieler.Create: Setze Spielertyp = 1.
    19.10.2009 09:00:14 -0- ....TSpieler.Create: Setze Vermoegen = 0.
    19.10.2009 09:00:14 -0- ....TSpieler.Create: Setze Name = Frank.
    19.10.2009 09:00:14 -0- ....TSpieler.Create: Setze Farbe = clYellow.
    19.10.2009 09:00:14 -0- ....TSpieler.Create: Erzeuge EinheitenListe.
    19.10.2009 09:00:14 -0- ......TEinheit.Create: ---START---
    19.10.2009 09:00:14 -0- ......TEinheit.Create: Setze NeuPreis = 1000.
    19.10.2009 09:00:14 -0- ......TEinheit.Create: Setze Zustand in Prozent = 100.
    19.10.2009 09:00:14 -0- ........TLaderaum.Create: ---START--- IN: iProzentZustand = 20
    19.10.2009 09:00:14 -0- ........TLaderaum.Create: ---ENDE---

    19.10.2009 09:00:14 -0- ......TEinheit.Create: Erzeuge Laderaum mit der Größe 20.
    19.10.2009 09:00:14 -0- ......TEinheit.Create: Setze Name = NCC 1701-E.
    19.10.2009 09:00:14 -0- ......TEinheit.Create: Setze Position = (-2 / -2).
    19.10.2009 09:00:14 -0- ......TEinheit.Create: Erzeuge Waffenliste.
    19.10.2009 09:00:14 -0- ......TEinheit.Create: ---ENDE---

    19.10.2009 09:00:14 -0- ....TSpieler.Create: Setze aktive Einheit.
    19.10.2009 09:00:14 -0- ....TSpieler.Create: ---ENDE---

    19.10.2009 09:00:14 -0- ....TSpiel.GibSpieler: ---START---
    19.10.2009 09:00:14 -0- ....TSpiel.GibSpieler: ---ENDE---

    19.10.2009 09:00:14 -8- ..TfrmStartGBH.Button1Click: slWaffenliste nicht angelegt oder leer.
    19.10.2009 09:00:14 -7- ..TfrmStartGBH.Button1Click: Lege eine Test-Waffe an.
    19.10.2009 09:00:14 -0- ....TWaffe.Create: ---START---
    19.10.2009 09:00:14 -0- ....TWaffe.Create: ---ENDE---

    19.10.2009 09:00:16 -0- ..TfrmStartGBH.FeuerButtonClick: ---ENDE---

Um das Event zu setzten müsste ich WAS tun?
Gruß Glühwürmchen
<><
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 1 von 2  1 2      


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 19:13 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz