![]() |
Auf Interfaces zugreifen?
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:
Wie greife ich jetzt auf das Interface zu?
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. 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? |
Re: Auf Interfaces zugreifen?
Delphi-Quellcode:
var
I: IAppInterface; begin I := TAppIntfImpl.Create; // u.U. kann auch soetwas gewollt sein: I := TKlasse.Create as ISchnittstelle; end; |
Re: Auf Interfaces zugreifen?
Zitat:
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:
Was will ich aber nun haben?
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. 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:
Ich habe meine Unit deshalb in diese Form hier geändert:
var
I: IAppInterface; begin //heute: I := TAppIntfImpl.Create; //und morgen I := TAppIntfImpl2.Create; end;
Delphi-Quellcode:
Wenn ich das starte, erhalte ich, was ich will, die Interfaced-Methode wird aufgerufen.
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. 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? . |
Re: Auf Interfaces zugreifen?
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:
Die USB-Schnittstelle:type ISchnittstelle = interface ( IInterface ) procedure Execute(); end; type TSchnittstelle = class ( TInterfacedObject, IInterface ) public procedure Execute(); virtual; abstract; end;
Delphi-Quellcode:
Die COM-Schnittstelle:
type
TUSBSchnittstelle = class ( TSchnittstelle ) private protected public procedure Execute(); override; end; procedure TUSBSchnittStelle.Execute(); begin // Do Something with USB end;
Delphi-Quellcode:
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):
type
TCOMSchnittstelle = class ( TSchnittstelle ) private protected public procedure Execute(); override; end; procedure TCOMSchnittstelle.Execute(); begin // Do Something with COM end;
Delphi-Quellcode:
So würde ich das z.b. in etwa realisieren, wenn z.b. der Benutzer die Auswahl hätte zwischen den Schnittstellen oder so.
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; 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* |
Re: Auf Interfaces zugreifen?
Zitat:
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. |
Re: Auf Interfaces zugreifen?
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:
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.
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; 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. :dp: |
Re: Auf Interfaces zugreifen?
Zitat:
"Why do interface implementations based on TComponent leak memory?" ![]() Denn: die Implementierung von _Release in TComponent führt kein free der Instanz aus... Cheers, |
Re: Auf Interfaces zugreifen?
Zitat:
|
Re: Auf Interfaces zugreifen?
Hier ein einfaches Beispiel, das ohne Referenzzählung auskommt und trotzdem mit Interface eine elegante Lösung bietet.
Delphi-Quellcode:
Die Funktionen in der Unit UDateioperationen können und sollen nicht wissen, wo diese später überall aufgerufen werden.
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; 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. |
Re: Auf Interfaces zugreifen?
Zitat:
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. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 19:56 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