AGB  ·  Datenschutz  ·  Impressum  







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

Auf Interfaces zugreifen?

Offene Frage von "viakt133"
Ein Thema von viakt133 · begonnen am 18. Feb 2010 · letzter Beitrag vom 25. Feb 2010
Antwort Antwort
Seite 1 von 2  1 2      
viakt133

Registriert seit: 16. Feb 2010
18 Beiträge
 
Lazarus
 
#1

Auf Interfaces zugreifen?

  Alt 18. Feb 2010, 16:33
Hallo,

ich lese grad im Delphi Treff Forum herum und bin dabei auf Interfaces gestoßen. Das hat mich zu folgendem Programmentwurf inspiriert:

Delphi-Quellcode:
unit UAppIntf;

interface

uses
  Classes, Sysutils;

type
  IAppInterface = Interface(IIinterface)
  ['{8FBE82FA-E3BA-4B8D-992D-315965BF5407}']
    procedure DoSomething;
  End;

implementation

end.

//Hier nun die Implementation

unit UAppIntfImpl;

interface

uses
  Classes, Sysutils, Dialogs, UAppIntf;

type
  TAppIntfImpl = class(TInterfacedObject, IAppInterface)
    procedure DoSomething;
  end;

implementation

{ TAppIntfImpl }

procedure TAppIntfImpl.DoSomething;
begin
  ShowMessage('the Interfaced method');
end;

end.

//Und hier nun will ich das Interface verwenden, ohne die Methode DoSomething noch mal
//implementieren zu müssen.

unit UAppIntfUser;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, UAppIntf, UAppIntfImpl;

type
  TForm1 = class(TForm)
  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

end.
Wie greife ich jetzt auf das Interface zu?

Wäre IAppintfImpl eine Klasse (dann TAppIntfImpl), sähe das ja so aus:

var
Instance: TAppIntfImpl;

begin
Instance := TAppIntfImpl.Create; // evtl. ...create(parameter);
end.

WIe aber mache ich das, wenn ich stattdessen das Interface verwenden will?
  Mit Zitat antworten Zitat
Panthrax

Registriert seit: 18. Feb 2005
286 Beiträge
 
Delphi 2010 Enterprise
 
#2

Re: Auf Interfaces zugreifen?

  Alt 18. Feb 2010, 16:37
Delphi-Quellcode:
var
  I: IAppInterface;
begin
  I := TAppIntfImpl.Create;
    // u.U. kann auch soetwas gewollt sein: I := TKlasse.Create as ISchnittstelle;
end;
"Es gibt keine schlimmere Lüge als die Wahrheit, die von denen, die sie hören, missverstanden wird."
  Mit Zitat antworten Zitat
viakt133

Registriert seit: 16. Feb 2010
18 Beiträge
 
Lazarus
 
#3

Re: Auf Interfaces zugreifen?

  Alt 18. Feb 2010, 18:17
Zitat von Panthrax:
Delphi-Quellcode:
var
  I: IAppInterface;
begin
  I := TAppIntfImpl.Create;
    // u.U. kann auch soetwas gewollt sein: I := TKlasse.Create as ISchnittstelle;
end;
Danke erst mal für die echt schnelle Antwort!

Jetzt tut sich aber ein weiteres Problem auf:

Wenn ich zum Instantiieren wieder, wie in der Antwort geschrieben, TAppIntfImpl.Create aufrufe, wozu dann das Interface?

Dann müsste ich ja das AppInterface in der Implementationsunit instantiieren, was ich jetzt auch gemacht habe und zwar so, wie in der geänderten Unit UAppIntfImpl:

Delphi-Quellcode:
unit UAppIntfImpl;

interface

uses
  Classes, Sysutils, Dialogs, UAppIntf;

type
  TAppIntfImpl = class(TInterfacedObject, IAppInterface)
    procedure DoSomething;
  end;

var
  //Diese Variable ist nun meine Klasseninstanz
  AppInterface: TAppIntfImpl;

implementation

{ TAppIntfImpl }

procedure TAppIntfImpl.DoSomething;
begin
  ShowMessage('the Interfaced method');
end;

initialization
  AppInterface := TAppIntfImpl.Create;

finalization
  AppInterface.Free;

end.
Was will ich aber nun haben?

Ich will das Interface ansprechen, als ob es die Klasse wäre.

So könnte ich dann mit exakt demselben Zugriff, ohne den unten stehenden Quelltext ändern zu müssen,
die Methode TAppIntfImpl.DoSomething ändern und die von jetzt an die geänderte Methode aufrufen.

Delphi-Quellcode:
var
  I: IAppInterface;
begin
  //heute:
  I := TAppIntfImpl.Create;
  //und morgen
  I := TAppIntfImpl2.Create;
end;
Ich habe meine Unit deshalb in diese Form hier geändert:

Delphi-Quellcode:
unit UAppIntfUser;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, UAppIntf, StdCtrls, UAppIntfImpl;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private-Deklarationen }
    FInterfaceExists: Boolean;
  public
    FInterface: IAppInterface;
    FInterfaced: TAppIntfImpl;
    { Public-Deklarationen }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
  if FInterfaceExists then
  AppInterface.DoSomething;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  FInterfaceExists := true;
  FInterfaced := AppInterface;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  FInterfaced.Free;
end;

end.
Wenn ich das starte, erhalte ich, was ich will, die Interfaced-Methode wird aufgerufen.
Allerdings erhalte ich eine Exception mit der Meldung "ungültige Zeigeroperation".

Wenn ich jedoch FInterfaced.Free im finalization Abschnitt der Unit UAppIntfImpl nicht aufrufe, kommt diese Exception nicht.

Habe deshalb probeweise auch die Instantiierung im Initialization Abschnitt meiner Implementations Unit UAppIntfImpl entfernt und ich kann trotzdem auf die Klasse zugreifen.

Wo gibt es weiterführende Literatur zum Thema?

Nun muss ich wahrscheinlich immer den Typ der Klasse, die an das Interface angeschlossen werden soll, mitschleppen? Oder gibt es noch eine elegantere Möglichkeit. Wenn ich ein anderes Appinteface
bauen will, wo zb. in der Methode FInterfaced.DoSomething der WinAmp augerufen wird und in einer anderen gleich aufgebauten Klasse der Windows Media Player. Gibt es da noch einen eleganteren Weg?


.
  Mit Zitat antworten Zitat
Alaitoc

Registriert seit: 24. Okt 2008
263 Beiträge
 
Delphi 7 Enterprise
 
#4

Re: Auf Interfaces zugreifen?

  Alt 18. Feb 2010, 18:35
Also ich interpertiere das einfach mal...

Sagen wir mal du hast zwei Schnittstellen über die du gehen willst, einmal USB und einmal COM.
Da erstellst du jeweils eine Klasse für, die das Interface implementiert und in dem Interface
sind halt alle allgemeingehaltenden Methoden drin.

Bsp:

Das Interface:

Delphi-Quellcode:

type
  ISchnittstelle = interface ( IInterface )
    procedure Execute();
  end;

type
  TSchnittstelle = class ( TInterfacedObject, IInterface )
  public
    procedure Execute(); virtual; abstract;
  end;
Die USB-Schnittstelle:

Delphi-Quellcode:
type
  TUSBSchnittstelle = class ( TSchnittstelle )
    private

    protected

    public
      procedure Execute(); override;
  end;

procedure TUSBSchnittStelle.Execute();
begin
  // Do Something with USB
end;
Die COM-Schnittstelle:

Delphi-Quellcode:
type
  TCOMSchnittstelle = class ( TSchnittstelle )
    private

    protected

    public
      procedure Execute(); override;
  end;

procedure TCOMSchnittstelle.Execute();
begin
  // Do Something with COM
end;
Und dann noch ne Factory die dir eins von beiden erstellt ( die Unterscheidung mache ich hier mit nem simplen String, geht natürlich auch anders):

Delphi-Quellcode:
type
 TSchnittstellenFactory = class ( TObject )
    private
      { Private-Deklarationen }

    protected
      { Protected-Deklarationen }

    public
      class function CreateSchnittstelle( sTyp: String ): ISchnittstelle;
      
  end;

class function CreateSchnittstelle( sTyp: String ): ISchnittstelle;
var
 coSchnittstelle: TSchnittstelle;
begin
  coDependency := nil;
  if sTyp = "USB" then
  begin
    coDependency := TUSBSchnittstelle.Create();
  end
  else if sTyp = "COM" then
  begin
    coDependency := TCOMSchnittstelle.Create();
  end;
  result := coDependency;
end;
So würde ich das z.b. in etwa realisieren, wenn z.b. der Benutzer die Auswahl hätte zwischen den Schnittstellen oder so.
Natürlich nur schnell hingeklatscht...aber sollte verdeutlichen was ich meine ^^

MfG Alaitoc

PS: Übernehme keine Garantie für irgendwelche Fehler, hab nu Feierabend *gg*
  Mit Zitat antworten Zitat
Benutzerbild von implementation
implementation

Registriert seit: 5. Mai 2008
940 Beiträge
 
FreePascal / Lazarus
 
#5

Re: Auf Interfaces zugreifen?

  Alt 18. Feb 2010, 19:01
Zitat:
Wenn ich jedoch FInterfaced.Free im finalization Abschnitt der Unit UAppIntfImpl nicht aufrufe, kommt diese Exception nicht.
Ich nehme einfach mal so an, dass FInterface und FInterfaced auf das gleiche Objekt zeigen. Dann brauchst du FInterfaced.Free gar nicht aufzurufen, da das Objekt sich selber freigibt.
Es sei denn, du schaltest mit irgendeinem Compilerschalter auf CORBA-Interfaces um, da läuft das anders. Ich gehe einfach mal von normalen COM-Interfaces aus.
  Mit Zitat antworten Zitat
Panthrax

Registriert seit: 18. Feb 2005
286 Beiträge
 
Delphi 2010 Enterprise
 
#6

Re: Auf Interfaces zugreifen?

  Alt 18. Feb 2010, 19:13
Hehe, Du machst mir den Eindruck als hättest Du Siebenmeilenstiefel an. Eins nach dem Anderen:

1) Hast Du mal versucht IAppInterface.Free aufzurufen? Geht nicht? Genau! Weil Free eben nicht Bestandteil der Schnittstelle ist. Bei Schnittstellen übernimmt Delphi die Referenzzählung. Wird die letzte Schnittstellenreferenz (Schnittstellenvariable) entfernt wird das Objekt automatisch freigegeben. Wer dann noch eine Objektreferenz (Objektvariable) auf das Objekt hat, hat dann "Pech gehabt", diese zeigt dann nämlich nur noch an die Stelle, wo vorher mal ein Objekt war. (Beliebte Ursache für Zugriffsverletzungen bei Neulingen auf diesem Gebiet.) Man sollte sich deshalb entscheiden, ein Objekt entweder über Schnittstellenvariablen oder über Objektvariablen anzusprechen. (Nur um es zu erwähnen: Es gibt auch Objekte die die Referenzzählung unterwandern. So eines wird aber nicht von Delphi mitgebracht.)

2) Objekte werden immer mit Create erzeugt, und immer mit Free freigegeben, unabhängig davon wie sie referenziert werden. Und noch einmal: Bei Schnittstellen entscheidet Delphi wann Free aufgerufen wird.

3) Ein wesentlicher Teil beim Programmieren besteht darin, für jeses Programmstück den richtigen Platz zu finden. Wenn man am Ende für alles einen Platz gefunden hat, und nichts doppelt oder ähnlich programmiert ist, kann man sich sicher sein, schon eine ganze Menge richtig gemacht zu haben. -- Zu Deinem Fall: Vielleicht ist möchtest Du Dein Hauprogramm mit einer Klasse parametrisieren. Schau Dir mal TClass an und denke daran den Kontruktor der Basisklasse zu überschreiben! Du kannst selbst so etwas hinzufügen:
Delphi-Quellcode:
type
  IKommunikation = interface end;
  TKommunikation = class(TInterfacedObject, IKommunikation)
    constructor Create; virtual;
  end;
  TKommunikationsklasse = class of TKommunikation;
  TUSBKommunikation = class(TKommunikation)
    constructor Create; override;
  end;
  TCOMKommunikation = class(TKommunikation)
    constructor Create; override;
  end;

procedure THauptprogramm.MachWas(const Kommunikationsklasse: TKommunikationsklasse);
var
  K: IKommunikation; // Schnittstellenvariable
begin
  K := Kommunikationsklasse.Create;
  // usw.
  // nicht: K.Free;
end;
4) Aus einer Schnittstelle kann i.d.R. das Objekt nicht wiedergewonnen werden. Dein "C := I;" wird deshalb nicht funktionieren. Außerdem: Es ist besser sich erst einmal festzulegen, Objekte entweder nur über Objektvariablen oder nur über Schnittstellenvariablen zu referenzieren. Wenigstens bis zum sicheren Umgang sollte man das wirklich beherzigen, sonst ist der Frust schnell da. Die Ursachen für Zugriffsverletzungen regelmäßig schwer zu auszumachen.

5) "Was muss ich da anders machen?" Eigentlich alles. Ich hoffe Du siehst jetzt ein bisschen klarer. Wenn nicht, lass es eine Weile setzen. Spiel ein bisschen mit Klassen, Vererbung, Methodenüberschreiben und Schnittstellen in einer Konsolenanwendung (schön einfach halten). Ein bisschen im Schrittbetrieb durchgehen, bis klar ist warum das so ist.

Versuche zukünftig Deine Probleme besser einzugrenzen, dann kann Dir auch besser geholfen werden. Du scheinst mir noch sehr zu schwimmen.

Du bist neu hier in der DP, also auch: Ein herzliches Willkommen.
"Es gibt keine schlimmere Lüge als die Wahrheit, die von denen, die sie hören, missverstanden wird."
  Mit Zitat antworten Zitat
mjustin

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

Re: Auf Interfaces zugreifen?

  Alt 18. Feb 2010, 20:52
Zitat von Panthrax:
(Nur um es zu erwähnen: Es gibt auch Objekte die die Referenzzählung unterwandern. So eines wird aber nicht von Delphi mitgebracht.)
TComponent fällt mir da als Gegenbeispiel ein, siehe hier:

"Why do interface implementations based on TComponent leak memory?"
http://stackoverflow.com/questions/2...nt-leak-memory

Denn: die Implementierung von _Release in TComponent führt kein free der Instanz aus...

Cheers,
Michael Justin
habarisoft.com
  Mit Zitat antworten Zitat
Panthrax

Registriert seit: 18. Feb 2005
286 Beiträge
 
Delphi 2010 Enterprise
 
#8

Re: Auf Interfaces zugreifen?

  Alt 19. Feb 2010, 11:15
Zitat von mjustin:
Zitat von Panthrax:
(Nur um es zu erwähnen: Es gibt auch Objekte die die Referenzzählung unterwandern. So eines wird aber nicht von Delphi mitgebracht.)
TComponent fällt mir da als Gegenbeispiel ein (...)
Stimmt, bei Komponenten ist deren Besitzer für die Freigabe zuständig (soweit es kein Com-Objekt ist). Ist mir gar nicht so direkt eingefallen, dabei habe schon damit gearbeitet.
"Es gibt keine schlimmere Lüge als die Wahrheit, die von denen, die sie hören, missverstanden wird."
  Mit Zitat antworten Zitat
Blup

Registriert seit: 7. Aug 2008
Ort: Brandenburg
1.477 Beiträge
 
Delphi 12 Athens
 
#9

Re: Auf Interfaces zugreifen?

  Alt 19. Feb 2010, 11:30
Hier ein einfaches Beispiel, das ohne Referenzzählung auskommt und trotzdem mit Interface eine elegante Lösung bietet.
Delphi-Quellcode:
unit UProgressHandler;

interface

type
  IProgressHandler = Interface(IIinterface)
  ['{...}']
    procedure ProgressStart;
    procedure Progress(AProzent: Integer; var IsAbort: Boolean);
    procedure ProgressEnd;
  End;

implementation

end.


unit UDateiOperationen;

interface

procedure KopiereDatei(const AQuelle, AZiel: string; const AProgressHandler: IProgressHandler);

implementation

procedure KopiereDatei(const AQuelle, AZiel: string; const AProgressHandler: IProgressHandler);
begin
  AProgressHandler.ProgressStart;
  try
    for {... irgendeine Schleife}
    begin
      {tu irgenwas}
      IsAbort := False;
      AProgressHandler.Progress(xProzent, IsAbort);
      if IsAbort then
        Exit;
    end;
  finally
    AProgressHandler.ProgressEnd;
  end;
end;

end.


unit UForm1;

interface

type
  TForm1 = class(TForm, IProgressHandler)
    FAborted: Boolean;
    procedure ButtonDateiKopierenClick(Sender: TObject);
    procedure ButtonAbortClick(Sender: TObject);
    {IProgressHandler}
    procedure ProgressStart;
    procedure Progress(AProzent: Integer; var IsAbort: Boolean);
    procedure ProgressEnd;
  end

implementation

procedure TForm1.ButtonDateiKopierenClick(Sender: TObject);
begin
  ButtonDateiKopieren.Enabled := False;
  ButtonAbort.Enabled := True;
  FAborted := False;
  try
    KopiereDatei(EditQuelle.Text, EditZiel.Text, Self);
  finally
    ButtonDateiKopieren.Enabled := True;
    ButtonAbort.Enabled := False;
  end;
end;

procedure TForm1.ButtonAbortClick(Sender: TObject);
begin
  FAborted := True;
  ButtonAbort.Enabled := False;
end;

procedure TForm1.ProgressStart;
begin
  FProgressBar1.Visible := True;
  FProgressBar1.Position := 0;
end;

procedure TForm1.Progress(AProzent: Integer; var IsAbort: Boolean);
begin
  FProgressBar1.Position := AProzent;
  IsAborted := FAborted;
end;

procedure TForm1.ProgressEnd;
begin
  FProgressBar1.Visible := False;
end;
Die Funktionen in der Unit UDateioperationen können und sollen nicht wissen, wo diese später überall aufgerufen werden.
Trotzdem können diese über das Interface auf den Aufrufer zugreifen und sogar indirekt auf externe Ereigniss (in diesem Fall Abbruch durch den Benutzer) reagieren.
  Mit Zitat antworten Zitat
Benutzerbild von sirius
sirius

Registriert seit: 3. Jan 2007
Ort: Dresden
3.443 Beiträge
 
Delphi 7 Enterprise
 
#10

Re: Auf Interfaces zugreifen?

  Alt 19. Feb 2010, 11:37
Zitat von Blup:
Hier ein einfaches Beispiel, das ohne Referenzzählung auskommt und trotzdem mit Interface eine elegante Lösung bietet.
Das ist genau die Art und Weise, wie man Ereignishandler in Java implementiert. Da geht es gar nicht ohne Interfaces (außer anonyme Methoden).

In Delphi sind Interfaces hauptsächlich für die Verwendung zwischen verschiedenen Modulen (EXE->DLL) notwendig. Wobei man da recht schnell zu COM kommt. Bei allen anderen Interfacelösungen muss man wegen der Referenzzählung höllisch aufpassen und sollte es deswegen (meiner Meinung nach) nur sehr selten einsetzen.
Dieser Beitrag ist für Jugendliche unter 18 Jahren nicht geeignet.
  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 16:01 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