Delphi-PRAXiS
Seite 3 von 4     123 4      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   Delphi Schon wieder: Warum Interfaces II (https://www.delphipraxis.net/204724-schon-wieder-warum-interfaces-ii.html)

Benmik 22. Jun 2020 19:07

AW: Schon wieder: Warum Interfaces II
 
Zitat:

Zitat von stahli (Beitrag 1468039)
Ich stelle Dir einfach mal eine Aufgabe:

Zurück auf die Schulbank!

Danke, stahli. Die Objekte nicht freigeben und dennoch kein Memoryleak erhalten - hm, da könnten doch nicht etwa Interfaces ins Spiel kommen...?! :thumb:

Ich probiere das später mal aus.

DeddyH 22. Jun 2020 19:36

AW: Schon wieder: Warum Interfaces II
 
Stop! Nur weil man Interfaces benutzt bedeutet das nicht zwangsläufig, dass die implementierenden Instanzen automatisch freigegeben werden. Wenn man das möchte, leitet man zweckmäßigerweise von TInterfacedObject ab, falls nicht, von TInterfacedPersistent.

Benmik 22. Jun 2020 20:19

AW: Schon wieder: Warum Interfaces II
 
Und kann das richtig sein, dass die Objekte direkt von TObject abgeleitet sein sollen - das müsste ja in einem Cast von Object zu Interface enden... ?

Stevie 22. Jun 2020 20:20

AW: Schon wieder: Warum Interfaces II
 
Bei beiden explizit genannten Bibliotheken bin ich mit fast 100%ig sicher, dass hier Interfaces genutzt werden, um die automatische Speicherverwaltung zu nutzen. Wenn ich
Delphi-Quellcode:
AsyncCall(...)
oder gleichermaßen, was aus der OTL aufrufe, dann wird der Klump nach ausführung automatisch weggeräumt und ich muss das nicht selbst freigeben, als ob ich mir eine eigene TThread Klasse gebaut hätte oder so. Ja, TThread hat FreeOnTerminate, aber in den Bibliotheken passiert ja bissle mehr

stahli 22. Jun 2020 20:46

AW: Schon wieder: Warum Interfaces II
 
Zitat:

Zitat von Benmik (Beitrag 1468045)
Und kann das richtig sein, dass die Objekte direkt von TObject abgeleitet sein sollen - das müsste ja in einem Cast von Object zu Interface enden... ?

Stimmt, Du solltest also von TInterfacedObject ableiten, aber alle davon. Das ist die kleinste Klasse, die Interfaces und ARC unterstützt.
Auf keinen Fall eine Basisklasse erstellen, die procedure Log einführt. (Warum, können wir später nochmal besprechen.)

bepe 22. Jun 2020 20:55

AW: Schon wieder: Warum Interfaces II
 
Weiteres Beispiel sind Binary übergreifende Objekte. So kann man die Funktionalität erweitern ohne eine wilde Sammlung von Methoden zu exportieren. Zum Beispiel bei Listen (ListViews oder Grids) habe ich gerne ein Popupmenu mit Funktionen. Wenn ich ein Untermenü zum Exportieren habe, kann ich dieses um Formate erweitern, in dem ich einfach eine weitere DLL ins Programmverzeichnis lege. Ein bisschen Pseudo Code:

Interfaces
Delphi-Quellcode:
  IList = interface
  ['{1359E96A-BE76-4466-AFBA-CAFA950A4052}']
    procedure First;
    procedure Prior;
    procedure Next;
    procedure Last;

    function FieldCount: Integer;
    function FieldCaption(AFieldNo: Integer): String;
    function FieldValue: Variant;
    function Eof: Boolean;

  end;

  IPlugin = interface
  ['{F2D42A98-ECC5-4633-A0E9-C90F1DAB7A88}']

  end;

  IPluginManager = interface
  ['{FB80046D-24EB-4214-966D-D0F97699B3FC}']
    procedure RegisterPlugin(APlugin: IPlugin);

  end;

  IListExporter = interface
  ['{B16E7F30-D6A7-4B83-870A-4E8B072949CC}']
    procedure ExportList(AList: IList);

    function Caption: String;

  end;
DLL Auszug
Delphi-Quellcode:
  TCsvExporter = class(TInterfacedObject, IPlugin, IListExporter)
  public
    procedure ExportList(AList: IList);

    function Caption: string;

  end;

procedure InitPlugin(AManager: IPluginManager);
begin
  AManager.RegisterPlugin(TCsvExporter.Create);
end;

exports
  InitPlugin;
Beim Programmstart wird die DLL geladen und die InitPlugin Prozedur aufgerufen.

Und so ungefähr wird das Menü aufgebaut
Delphi-Quellcode:
var
  CurrentPlugin: IPlugin;
  tmpExporter: IListExporter;
begin
  for CurrentPlugin in tmpManager.Plugins do
  begin
    if CurrentPlugin.GetInterface(IListExporter, tmpExporter) then
      pmExport.Add(tmpExport.Caption);
  end;

Eine weitere Möglichkeit sind .Net Assemblys. Es gibt einige Frameworks für .Net die es für Delphi gar nicht oder nicht annährend in vergleichbarer Qualität gibt. Die benötigte Funktionalität kapsele ich dann in einer Klasse und diese wird per Interface bereitgestellt.

Und noch ein (für uns/mich) sehr wichtiges Beispiel fällt mir ein: Delphi's Tools API (auch nur ein Plugin Beispiel).

(In der echten Welt muss man natürlich noch auf Aufrufkonventionen und Datentypen/Marshalling achten....)

Benmik 22. Jun 2020 21:53

AW: Schon wieder: Warum Interfaces II
 
Zitat:

Zitat von Stevie (Beitrag 1468046)
Bei beiden explizit genannten Bibliotheken bin ich mit fast 100%ig sicher, dass hier Interfaces genutzt werden, um die automatische Speicherverwaltung zu nutzen.

Vielen Dank für deine Mühe, Stefan. War auch meine Vermutung.

freimatz 23. Jun 2020 13:09

AW: Schon wieder: Warum Interfaces II
 
Zitat:

Zitat von Benmik (Beitrag 1468037)
Ich habe ja keinerlei Zweifel, dass es Sinn macht! Nur sind in aller Regel die genannten Anwendungsbereiche außerhalb meiner Einzel-Bastel-Sphäre.

Dann brauchst Du auch keine interfaces (vielleicht von COM mal abgesehen).

Benmik 23. Jun 2020 15:54

AW: Schon wieder: Warum Interfaces II
 
Zitat:

Zitat von freimatz (Beitrag 1468093)
Dann brauchst Du auch keine interfaces (vielleicht von COM mal abgesehen).

Ja, vermutlich nicht, aber das kann ich halt erst beurteilen, wenn mir der Einsatz von Interfaces genügend klar ist.

Hier ist jetzt mal die "Hausaufgabe" von stahli (die Anzahl der geforderten Objekte von 5 hat mich zu einer freien Interpretation der Aufgabe angeregt):
Delphi-Quellcode:
unit Unit2;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls,System.Generics.Collections,System.UITypes;

type
  TForm2 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    procedure WMSysCommand(var Message: TMessage); message WM_SYSCOMMAND;
  public
    { Public-Deklarationen }
  end;

  IDrafi = Interface ['{876A9480-49D2-4735-A95F-AF21015CF372}']
    function Kracks:integer;
  end;

  TMarmor = class(TInterfacedObject,IDrafi)
    function Kracks:integer;
  end;

  TStein = class(TInterfacedObject,IDrafi)
    function Kracks:integer;
  end;

  TUnd = class(TInterfacedObject,IDrafi)
    function Kracks:integer;
  end;

  TEisen = class(TInterfacedObject,IDrafi)
    function Kracks:integer;
  end;

  TBricht = class(TInterfacedObject,IDrafi)
    function Kracks:integer;
  end;

  procedure UnsereLiebe(LstDrafi:TList<IDrafi>);

var
  Form2: TForm2;
  LstDrafi:TList<IDrafi>;

implementation

{$R *.dfm}

procedure TForm2.Button1Click(Sender: TObject);
var i,n:integer;
    Drafi:IDrafi;
begin
  If Assigned(LstDrafi)
    then FreeAndNil(LstDrafi);
  LstDrafi := TList<IDrafi>.Create;
  LstDrafi.Capacity := 100;
  For i := 1 to 20 do begin
    For n := 1 to 5 do begin
      Case n of
        1: Drafi := TMarmor.Create;
        2: Drafi := TStein.Create;
        3: Drafi := TUnd.Create;
        4: Drafi := TEisen.Create;
        5: Drafi := TBricht.Create;
      End;
      LstDrafi.Add(Drafi);
    end;
  end;
  UnsereLiebe(LstDrafi);
end;

procedure UnsereLiebe(LstDrafi:TList<IDrafi>);
var i:integer;
begin
  For i := 0 to LstDrafi.Count - 1 do
    If LstDrafi[i].Kracks = mrCancel
      then break;
end;

function TMarmor.Kracks:integer;
begin
  Result := mrOK;
  Showmessage('Marmor...');
end;

function TStein.Kracks:integer;
begin
  Result := mrOK;
  Showmessage('...Stein...');
end;

function TUnd.Kracks:integer;
begin
  Result := mrOK;
  Showmessage('...und...');
end;

function TEisen.Kracks:integer;
begin
  Result := mrOK;
  Showmessage('...Eisen bricht.');
end;

function TBricht.Kracks:integer;
begin
  Result := MessageDlg('Abää unsärä Liihiebe nischt!',mtInformation,[mbOk,mbCancel], 0, mbOk);
end;

procedure TForm2.WMSysCommand(var Message: TMessage);
begin
  If Message.WParam = SC_CLOSE
    then LstDrafi.Free;
  inherited;
end;

end.
Ich muss schon sagen, dass ich das dann doch nicht so locker hingeworfen habe, wie ich zuerst gedacht hatte. Knackpunkt war
Delphi-Quellcode:
Drafi := TMarmor.Create;
. Die Tatsache, dass ein Interface ganz verschiedene Klassen aufnehmen kann, war mir natürlich klar, aber erst bei der konkreten Implementation (die TList kannte ja keine Typen) kam der Aha-Effekt.
Jetzt kann ich mich vielleicht mal (wieder!) diesem hier zuwenden.

stahli 23. Jun 2020 16:25

AW: Schon wieder: Warum Interfaces II
 
Super, so soll es sein. Durch praktische Verwendung habe ich es auch erst kapiert. Dann wieder nicht .... dann wieder ja ... dann doch irgendwie wieder nicht ..... und irgendwann war es verinnerlicht.

Wenn Du magst, können wir die Aufgabe noch etwas erweitern:

Erstelle noch zwei Interfaces:

IDoA mit Methode A und
IDoB mit Methode B:

Den ersten zwei Klassen weist Du ZUSÄTZLICH IDoA und dem ersten und den letzten 2 IDoB zu.

Dann rufst Du für alle Objekte von den 100 (die IDoA unterstützen) A auf und analog noch B.

Dazu musst Du Dir "Supports" anschauen. Das ist zwar auch ein Cast, aber etwas anders als bei Klassen und durchaus mit ein paar Vorteilen...


Alle Zeitangaben in WEZ +1. Es ist jetzt 06:14 Uhr.
Seite 3 von 4     123 4      

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