Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   Auf Interfaces zugreifen? (https://www.delphipraxis.net/147899-auf-interfaces-zugreifen.html)

viakt133 18. Feb 2010 15:33


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:
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?

Panthrax 18. Feb 2010 15:37

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;

viakt133 18. Feb 2010 17:17

Re: Auf Interfaces zugreifen?
 
Zitat:

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?


.

Alaitoc 18. Feb 2010 17:35

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:

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*

implementation 18. Feb 2010 18:01

Re: Auf Interfaces zugreifen?
 
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.

Panthrax 18. Feb 2010 18:13

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:
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. :dp:

mjustin 18. Feb 2010 19:52

Re: Auf Interfaces zugreifen?
 
Zitat:

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,

Panthrax 19. Feb 2010 10:15

Re: Auf Interfaces zugreifen?
 
Zitat:

Zitat von mjustin
Zitat:

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. :cyclops:

Blup 19. Feb 2010 10:30

Re: Auf Interfaces zugreifen?
 
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.

sirius 19. Feb 2010 10:37

Re: Auf Interfaces zugreifen?
 
Zitat:

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.

mjustin 19. Feb 2010 12:09

Re: Auf Interfaces zugreifen?
 
Zitat:

Zitat von sirius
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.

Interfaces verwende ich eigentlich 'höllisch gerne' :)

Prkatisch sind sie zum Beispiel bei der XML API, wenn man nach Belieben eine Library durch eine andere ersetzen kann, da die API einheitlich ist. Wenn ein Anbieter nette Extrafeatures hat, oder die Library besonders schlank ist, muss man nicht erst drei Tage lang refaktorisieren :)

Und mit Interfaces kann man das gut von der Festlegung auf vorgegebene Klassenhierarchien trennen. Java macht das schon seit Ewigkeiten (JDBC, Mail-API, Servlet-API JMS, ...), und die Hersteller der dazu passenden Implementierungen freuen sich, dass sie eine stabile Vorgabe haben - Design by Contract sozusagen - die den berüchtigten Vendor-Lock-In verhindert. Doch bei Delphi gibt es natürlich auch Teile, die ähnlich arbeiten, dbExpress zum Beispiel, keine Frage.

Wenn Delphi ein Problem mit Interfaces hat, dann eher weil der Anwender sich vertun kann bei der Mischung von Objekt- und Interfacereferenzen, oder wenn TComponent eigene Besitzerverwaltung praktiziert (und man das nicht bedenkt). Fazit: ich liebe Interfaces.

Viele Grüße,
Michael

sirius 19. Feb 2010 12:15

Re: Auf Interfaces zugreifen?
 
Zitat:

Zitat von mjustin
Java macht das schon seit Ewigkeiten

Ja, in Java finde ich Interfaces auch sehr schön. Aber Java hat einen GC, der die Verwendung extrem vereinfacht. Man muss auf gewisse Dinge nicht aufpassen, welche du auch teilweise nennst:
Zitat:

Wenn Delphi ein Problem mit Interfaces hat, dann eher weil der Anwender sich vertun kann bei der Mischung von Objekt- und Interfacereferenzen, oder wenn TComponent eigene Besitzerverwaltung praktiziert (und man das nicht bedenkt). Fazit: ich liebe Interfaces.
Wenn man auf alles Obacht gibt, dann sind Interfaces auch ne tolle Sache. Ober das man in Delphi Obacht geben muss ist minder toll und es kann sehr blöde Fehler geben.

viakt133 20. Feb 2010 15:56

Re: Auf Interfaces zugreifen?
 
Hallo,

bin wieder bei den Interfaces angekommen. Hab jetzt diesen Ansatz:

Delphi-Quellcode:
unit UAppIntfFactory;

interface

uses
  Classes, Sysutils, Contnrs, UAppIntf;

var
  Interfaces: TInterfaceList; //In DElphi eingebaut
  ClassesIntf: TObjectList;  //In DElphi eingebaut

  Interfaced: IAppInterface;

procedure SetClassOf(Instance: TObject);
procedure SetInterfaceOf(Name: String);
function GetInterfaceOf(Name: String): IInterface;
function GetClassOf(Instance: TObject): TObject;

implementation

uses UAppIntfImpl;

procedure SetClassOf(Instance: TObject);
begin
  if Assigned(ClassesIntf) then
  begin
    ClassesIntf.Add(Instance);
  end;
end;

procedure SetInterfaceOf(Name: String);
begin
  if Assigned(Interfaces) then
  begin
    Interfaces.Add(Interfaced);
  end;
end;

function GetClassOf(Instance: TObject): TObject;
var Index: Integer;
begin
  if Assigned(ClassesIntf) then
  begin
    Index := 0;
    while Index < ClassesIntf.Count do
    begin
      {
      if Instance is Classesintf.Items[index] then
      begin
        Result := ClassesIntf.Items[Index];
        Index := ClassesIntf.Count;
      end;
      }
      Inc(Index);
    end;
  end;
end;

function GetInterfaceOf(Name: String): IInterface;
var Index: Integer;
begin
  if Assigned(Interfaces) then
  begin

    Result := Interfaces.Items[Index];
  end;
end;

initialization
  Interfaces := TInterfaceList.Create;
  ClassesIntf := TObjectList.Create;

finalization
  ClassesIntf.Free;
  Interfaces.Free;

end.

//Hier die Implementation der Klassen:

unit UAppIntfImpl;

interface

uses
  Classes, Sysutils, Dialogs, UAppIntf, UAppIntfFactory;

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

  TAppIntfImpl2 = class(TInterfacedObject, IAppInterface)
    procedure DoSomething;
  end;

var
  InterfacedClass1: TAppIntfImpl;
  InterfacedClass2: TAppIntfImpl2;

implementation

{ TAppIntfImpl }

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

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

initialization
  //InterfacedClass := TAppIntfImpl.Create;
  if Assigned(Interfaces) then
  begin
  Interfaces.Add( InterfacedClass1 );
  Interfaces.Add( InterfacedClass2 );
  end;
  if Assigned(ClassesIntf) then
  begin
    ClassesIntf.Add(InterfacedClass1);
    ClassesIntf.Add(InterfacedClass2);
  end;

  //InterfacedClass.Free;

end.


//Hier kommt die Anwendung:

unit UAppIntfUser;

interface

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

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

var
  Form1: TForm1;

implementation

{$R *.dfm}

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

procedure TForm1.cbxIntfChangeCloseUp(Sender: TObject);
var Index: Integer;
begin
  Index := cbxIntfChange.ItemIndex;
  cbxIntfChange.Text := cbxIntfChange.Items.Strings[Index];
  //FInterfaced := Interfaces.Items[Index];
  //------ Hier kommt eine Listen-Index-Exception, die ich mir nicht erklären kann
  FInterface2 := TAppIntfImpl(ClassesIntf.Items[index]);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  FInterfaceExists := true;
  //FInterfaced := GetInterfaceOf('Impl2');
end;

end.

Blup 23. Feb 2010 16:50

Re: Auf Interfaces zugreifen?
 
Falls du das bis jetzt überlesen hast: NIEMALS! Objektvariablen und Interfacevariablen für ein und das selbe Objekt.

Wenn deine Faktory die Objekte erzeugen soll, die IAppInterface unterstützen, dann gehören die Units die diese Objekte deklarieren, nirgends sonst eingebunden. Die konkrete Implementation geht niemanden etwas an, maximal die Faktory kennt diese, um die Objekte zu erzeugen. Die Faktory liefert IAppInterface, also sind alle Variablen außerhalb auch IAppInterface.

Hier mal eine Skizze wie eine Faktory funktionieren könnte:
Delphi-Quellcode:
unit UAppIntfDef;

type
  TCustomAppInterface = class(TInterfacedObject, IAppInterface)
    constructor Create; virtual; // virtualer Constructor für die Faktory
    procedure DoSomething; virtual; abstract;
  end;

  TAppInterfaceClass = class of TCustomAppInterface;

implementation


{********************}

unit UAppIntfFactory;

uses
   UAppIntfDef;

function RegisterAppInterface(AClass: TAppInterfaceClass);
procedure GetAppInterfaces(AItems: TStrings);
function GetAppInterface(AName: string): IAppInterface;

implementation

var
  FClasses: TClassList;

function RegisterAppInterface(AClass: TAppInterfaceClass);
begin
  FClasses.Add(AClass);
end;

procedure GetAppInterfaces(AItems: TStrings);
begin
  AItems.Clear;
  for i := 0 to FClasses.Count - 1 do
  begin
    AItems.Add(FClasses[i].ClassName;
  end;
end;

function GetAppInterface(AName: string): IAppInterface;
begin
  for i := 0 to FClasses.Count - 1 do
  begin
    if FClasses[i].ClassName = AName then
    begin
      Result := TAppInterfaceClass(FClasses[i]).Create;
      Exit;
    end;
  end;
end;

{********************}

unit UAppIntfImpl1;

interface

implementation

uses
  UAppIntfDef, UAppIntfFactory;

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

  procedure TAppIntfImpl.DoSomething;
  begin

  end;

begin
  RegisterAppInterface(TAppIntfImpl);
end.

{********************}

unit UAppIntfImpl2;

interface

implementation

uses
  UAppIntfDef, UAppIntfFactory;

type
  TAppIntfImp2 = class(TInterfacedObject, IAppInterface)
    constructor Create; override;
    procedure DoSomething;
  end;

  constructor TAppIntfImp2.Create;
  begin

  end;

  procedure TAppIntfImp2.DoSomething;
  begin

  end;

begin
  RegisterAppInterface(TAppIntfImp2);
end.

{********************}

unit UAppIntfUser;

type
  TForm1 = class(TForm)

  private
    FInterface: IAppInterface;
  end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  FInterface := GetAppInterface('TAppIntfImp1');
end;

procedure TForm1.RadioButtonClick(Sender: TObject);
var
  s: string;
begin
  if     Sender = RadioButton1 then s := 'TAppIntfImp1'
  else if Sender = RadioButton2 then s := 'TAppIntfImp2'
  else
    Exit;

  FInterface := GetAppInterface(s);
end;

procedure TForm1.ButtonClick(Sender: TObject);
begin
  FInterface.DoSomething;
end;
Classname ist natürlich keine schöne Unterscheidung der Klassen, aber dafür kann man in TCustomAppInterface ja ein eigenes class Property / class Methode vorsehen.

Hawkeye219 23. Feb 2010 18:36

Re: Auf Interfaces zugreifen?
 
Hallo,

die Funktion GetAppInterface sollte auch dann ein definiertes Ergebnis liefern, wenn man ihr einen unbekannten Klassennamen übergibt. Alternativ könnte sie natürlich eine Exception auslösen.

Gruß Hawkeye

Blup 24. Feb 2010 14:48

Re: Auf Interfaces zugreifen?
 
GetAppInterface gibt hier nil zurück, wenn ein unbekannter Klassenname angegeben wird.
Im Prinzip hast du mit der Exception recht, aber das ist wie gesagt nur eine Skizze.

Hawkeye219 24. Feb 2010 17:16

Re: Auf Interfaces zugreifen?
 
Hallo Blup,

Zitat:

Zitat von Blup
GetAppInterface gibt hier nil zurück, wenn ein unbekannter Klassenname angegeben wird.

Bei der Verwendung lokaler Variablen sieht das möglicherweise anders aus:

Delphi-Quellcode:
function F1: IInterface;
begin
  Result := TInterfacedObject.Create;
end;

function F2: IInterface;
begin
end;

procedure P1;
var
  I: IInterface;
begin
  I := F1();
  if Assigned(I) then
    ShowMessage ('nach F1(): I <> nil');

  I := F2();
  if Assigned(I) then
    ShowMessage ('nach F2(): I <> nil');
end;
Die Erklärung dieses Verhaltens liefert Alex Ciobanu in seinem Blog.

Gruß Hawkeye

Blup 25. Feb 2010 08:01

Re: Auf Interfaces zugreifen?
 
Gut zu wissen, schlecht das der Compiler für diesen Fall keine Warnmeldung gibt.
Delphi-Quellcode:
function GetAppInterface(AName: string): IAppInterface;
begin
  for i := 0 to FClasses.Count - 1 do
  begin
    if FClasses[i].ClassName = AName then
    begin
      Result := TAppInterfaceClass(FClasses[i]).Create;
      Exit;
    end;
  end;
  Result := nil;
end;

himitsu 25. Feb 2010 08:16

Re: Auf Interfaces zugreifen?
 
@Hawkeye219: Das selbe Verhalten gibt es übrigens auch bei Strings und dynamischen Arrays.

[edit]
ich seh grad, in der verlinkten Seite geht es um die Strings (dachte es geht da um das Interface),
aber ist ja bei allem das Selbe.


Alle Zeitangaben in WEZ +1. Es ist jetzt 06:38 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