AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Tutorials Tutorial Interfaces
Tutorial durchsuchen
Ansicht
Themen-Optionen

Tutorial Interfaces

Ein Tutorial von Fritzew · begonnen am 12. Apr 2017 · letzter Beitrag vom 11. Okt 2017
Antwort Antwort
Seite 1 von 2  1 2      
Benutzerbild von DeddyH
DeddyH

Registriert seit: 17. Sep 2006
Ort: Barchfeld
27.659 Beiträge
 
Delphi 12 Athens
 
#1

AW: Tutorial Interfaces

  Alt 12. Apr 2017, 10:58
Man muss sich einfach darüber klar sein, dass ein Interface ganz bewusst nichts Konkretes ist, sondern lediglich eine Zusicherung. Das bedeutet, der Benutzer (im Sinne einer Klasse) des Interfaces muss die konkrete Implementation überhaupt nicht kennen, kann sich aber trotzdem sicher sein, dass alle im Interface deklarierten Properties und Methoden vorhanden sind. Dadurch wird eine einfache Erweiterbarkeit erreicht. Ein kleines Beispiel: nehmen wir an, wir haben die Klassen TDings und TConsumer. Letztere ruft eine Methode Blubb der Ersteren auf.
Delphi-Quellcode:
type
  TDings = class
  public
    procedure Blubb;
  end;

  TConsumer = class
  public
    procedure Wuppdi(const Dings: TDings);
  end;

...

procedure TConsumer.Wuppdi(const Dings: TDings);
begin
  Dings.Blubb;
end;
Alles cool, alles easy. Nun wollen wir das Programm erweitern und schreiben eine Klasse TBums, die ebenfalls eine Methode Blubb enthält. Die Consumer-Klasse soll sowohl mit TDings als auch mit TBums umgehen können. Was können wir tun? Mir fallen da ein paar Möglichkeiten ein:
- Überladen der Wuppdi-Methode
- Ableiten der TDings- und TBums-Klasse von einer gemeinsamen Elternklasse, die Blubb als (ggf. virtuelle oder dynamische) Methode einführt. Das macht dann eine Änderung des Parametertyps in den Typ der Elternklasse notwendig.
- Ändern des Parametertyps in TObject und Abfrage in der Methode, ob es sich um TDings oder TBums handelt, dann Typecast und Aufruf.

Das ist alles nicht so wirklich elegant und macht Änderungen an verschiedenen Stellen nötig. Hier kommen jetzt die Interfaces ins Spiel: man ändert einmalig den Parametertyp in einen Interfacetyp, in dem die Blubb-Methode vereinbart wird. TDings und TBums implementieren jetzt dieses Interface, TConsumer greift dann darüber auf die Methode zu. Soll später eine weitere Klasse TSchiessMichTot dazukommen, lässt man sie ebenfalls das Interface implementieren, schon kann TConsumer ohne jede Codeänderung auch damit umgehen.

Delphi-Quellcode:
type
  ISuperIntf = interface
    ['{01107754-046D-4A32-AC6F-D96BC2AECCE0}']
    procedure Blubb;
  end;

  TDings = class(TInterfacedObject, ISuperIntf)
  public
    procedure Blubb;
  end;

  TBums = class(TInterfacedObject, ISuperIntf)
  public
    procedure Blubb;
  end;

...

procedure TConsumer.Wuppdi(const SuperIntf: ISuperIntf);
begin
  SuperIntf.Blubb;
end;
Detlef
"Ich habe Angst vor dem Tag, an dem die Technologie unsere menschlichen Interaktionen übertrumpft. Die Welt wird eine Generation von Idioten bekommen." (Albert Einstein)
Dieser Tag ist längst gekommen
  Mit Zitat antworten Zitat
Fritzew

Registriert seit: 18. Nov 2015
Ort: Kehl
678 Beiträge
 
Delphi 11 Alexandria
 
#2

AW: Tutorial Interfaces

  Alt 12. Apr 2017, 12:14
Hallo zusammen, ich habe das Beispiel noch mit einer DB Lösung erweitert. Vielleicht wird es dann besser zu verstehen
Fritz Westermann
  Mit Zitat antworten Zitat
Bbommel

Registriert seit: 27. Jun 2007
Ort: Köln
669 Beiträge
 
Delphi 12 Athens
 
#3

AW: Tutorial Interfaces

  Alt 12. Apr 2017, 12:39
Das ist alles nicht so wirklich elegant und macht Änderungen an verschiedenen Stellen nötig. Hier kommen jetzt die Interfaces ins Spiel: man ändert einmalig den Parametertyp in einen Interfacetyp, in dem die Blubb-Methode vereinbart wird. TDings und TBums implementieren jetzt dieses Interface, TConsumer greift dann darüber auf die Methode zu. Soll später eine weitere Klasse TSchiessMichTot dazukommen, lässt man sie ebenfalls das Interface implementieren, schon kann TConsumer ohne jede Codeänderung auch damit umgehen.
[/DELPHI]
Ich wurde trotz vieler Diskussionen bisher noch nicht von der "interface-Fraktion" überzeugt. Gerade dein Beispiel zeigt wunderbar, finde ich, dass man das gleiche Abstraktionslevel auch mit Klassen mit virtuellen und ggf. abstrakten Methoden erreichen kann. Du schreibst, dann müsse man in deinem Beispiel aber den Parametertyp von wuppdi ändern. Das muss man bei der Einführung des interface aber auch. Dafür fängt man sich bei den interfaces aber zunächst mal ein weiteres Sprachkonstrukt ein (was ja gewollt sein kann, wegen Referenzzählung, muss man dann aber auch wissen).

Ich finde interfaces immer dann so richtig überzeugend, wenn es wirklich um Schnittstellen geht (DLL, SOAP, ActiveX, ...)...

Aber das sind nur meine zwei Cent. Ich konnte bisher alles - abgesehen von der Referenzzählung - auch mit abstrakten/virtuellen Methoden erreichen kann.
  Mit Zitat antworten Zitat
Benutzerbild von DeddyH
DeddyH

Registriert seit: 17. Sep 2006
Ort: Barchfeld
27.659 Beiträge
 
Delphi 12 Athens
 
#4

AW: Tutorial Interfaces

  Alt 12. Apr 2017, 13:32
Wobei Du bei "reinen" Klassen aber immer gezwungen bist, eine festgelegte Hierarchie einzuhalten, es sei denn, Du prüfst mit "is-Orgien" jeden geeigneten Typ ab. Und wieso meinst Du, dass man einen Interface-Parameter jemals wieder ändern muss? Genau dafür ist er ja da, dass das eben nicht nötig ist.
Detlef
"Ich habe Angst vor dem Tag, an dem die Technologie unsere menschlichen Interaktionen übertrumpft. Die Welt wird eine Generation von Idioten bekommen." (Albert Einstein)
Dieser Tag ist längst gekommen
  Mit Zitat antworten Zitat
Bbommel

Registriert seit: 27. Jun 2007
Ort: Köln
669 Beiträge
 
Delphi 12 Athens
 
#5

AW: Tutorial Interfaces

  Alt 12. Apr 2017, 13:44
Das ist mir jetzt noch nicht ganz klar, was du meinst. Um mal bei deinem Beispiel zu bleiben - mit einer abstrakten Klasse sieht das ja so aus:

Delphi-Quellcode:
type
  TSuperClass = class
    procedure Blubb; virtual; abstract;
  end;

  TDings = class(TSuperClass)
  public
    procedure Blubb; override;
  end;

  TBums = class(TSuperClass)
  public
    procedure Blubb; override;
  end;

...

procedure TConsumer.Wuppdi(const SuperClass: TSuperClass);
begin
  SuperClass.Blubb;
end;
Ich meine nicht, dass man den interface-Parameter jemals wieder ändern muss - ich hatte mich nur auf deine Aussage bezogen, dass man den Parameter halt einmal ändern muss, wenn man das interface neu einführt. Genauso ist es ja hier mit dem Parameter für die abstrakte Klasse: einmal muss man ihn ändern, aber danach nicht mehr.

Und was meinst du hier mit "Hierarchie einhalten"? Ich muss von der abstrakten Basisklasse ableiten - genauso wie du das interface implementieren musst. Das gibt sich doch eigentlich nichts.
  Mit Zitat antworten Zitat
Benutzerbild von DeddyH
DeddyH

Registriert seit: 17. Sep 2006
Ort: Barchfeld
27.659 Beiträge
 
Delphi 12 Athens
 
#6

AW: Tutorial Interfaces

  Alt 12. Apr 2017, 13:49
Kannst Du immer und überall sicherstellen, dass die übergebenen Instanzen von einer bestimmten Klasse abgeleitet wurden? In meinem Minimalbeispiel ist das natürlich kein Problem, aber was ist z.B. mit Ableitungen bestehender VCL-Controls? Da bliebe nur die Lösung mit den zig "is"-Abfragen, wenn man auf Interfaces verzichten will. Wenn nicht, lässt man die Ableitung einfach das Interface implementieren, fertig.
Detlef
"Ich habe Angst vor dem Tag, an dem die Technologie unsere menschlichen Interaktionen übertrumpft. Die Welt wird eine Generation von Idioten bekommen." (Albert Einstein)
Dieser Tag ist längst gekommen
  Mit Zitat antworten Zitat
Bbommel

Registriert seit: 27. Jun 2007
Ort: Köln
669 Beiträge
 
Delphi 12 Athens
 
#7

AW: Tutorial Interfaces

  Alt 12. Apr 2017, 14:05
Hm, du meinst, dass du sowas machen könntest?
Delphi-Quellcode:
  TRumsButton = class(TButton,ISuperIntf)
  public
    procedure Blubb;
  end;

[...]
var
  customer: TCustomer;
  rums: TRumsButton;
begin
[...]
  customer.wuppdi(rums);
[...]
end;
Wäre das von der Synatx her richtig und das, was du meinst? Okay, zugegeben, das ginge mit abtrakten Klassen nicht.

PS: Edith hat sich gemeldet, aber ich poste es dennoch mal, um bei den vorherigen, ganz einfachen Beispielen zu bleiben.

Geändert von Bbommel (12. Apr 2017 um 14:07 Uhr) Grund: Überflüssiges override raus - copy&paste-Fehler :)
  Mit Zitat antworten Zitat
ISurf

Registriert seit: 1. Mär 2016
6 Beiträge
 
#8

AW: Tutorial Interfaces

  Alt 12. Apr 2017, 13:59
Und was meinst du hier mit "Hierarchie einhalten"? Ich muss von der abstrakten Basisklasse ableiten - genauso wie du das interface implementieren musst. Das gibt sich doch eigentlich nichts.
Genau da wird es interessant, sobald man nämlich bei einem komplexeren Projekt an den Punkt kommt, wo eine Klasse mehrere Interfaces implementieren muss, wäre eine Basisklasse nicht mehr ausreichend. Statt Mehrfachvererbung muss man dann eben Interfaces verwenden.

Interfaces sind in der Hinsicht flexibler, weil sie von beliebigen Klassen implementiert werden können und man dann z.B. bei einem Funktionsaufruf nur das entsprechende Interface als Parameter übergibt, dadurch kann man so einer Funktion auch beliebige Objekte übergeben, die keine gemeinsame Basisklasse brauchen.
  Mit Zitat antworten Zitat
Benutzerbild von DeddyH
DeddyH

Registriert seit: 17. Sep 2006
Ort: Barchfeld
27.659 Beiträge
 
Delphi 12 Athens
 
#9

AW: Tutorial Interfaces

  Alt 12. Apr 2017, 14:02
Exakt, ich habe da mal schnell ein Beispiel gebaut (ich habe die Standard-Benennung einfach beibehalten, da die Komponenten eh keinen tieferen Sinn haben):
Delphi-Quellcode:
unit Unit6;

interface

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

type
  IDisplay = interface
    ['{B515DBE1-9D0E-4491-BEE8-85805852870B}']
    function DisplayString: string;
  end;

  TEdit = class(VCL.StdCtrls.TEdit, IDisplay)
  public
    function DisplayString: string;
  end;

  TLabel = class(VCL.StdCtrls.TLabel, IDisplay)
  public
    function DisplayString: string;
  end;

  TComboBox = class(VCL.StdCtrls.TComboBox, IDisplay)
  public
    function DisplayString: string;
  end;

  TWuppdi = class
    procedure Consume(const Display: IDisplay);
  end;

  TForm6 = class(TForm)
    Edit1: TEdit;
    ComboBox1: TComboBox;
    Label1: TLabel;
    Button1: TButton;
    Button2: TButton;
    Button3: TButton;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
  private
    { Private-Deklarationen }
    FWuppdi: TWuppdi;
  public
    { Public-Deklarationen }
  end;

var
  Form6: TForm6;

implementation

{$R *.dfm}

{ TEdit }

function TEdit.DisplayString: string;
begin
  Result := Text;
end;

{ TLabel }

function TLabel.DisplayString: string;
begin
  Result := Caption;
end;

{ TComboBox }

function TComboBox.DisplayString: string;
begin
  if ItemIndex > -1 then
    Result := Items[ItemIndex]
  else
    Result := '';
end;

{ TWuppdi }

procedure TWuppdi.Consume(const Display: IDisplay);
begin
  ShowMessage(Display.DisplayString);
end;

procedure TForm6.Button1Click(Sender: TObject);
begin
  FWuppdi.Consume(Edit1);
end;

procedure TForm6.Button2Click(Sender: TObject);
begin
  FWuppdi.Consume(ComboBox1);
end;

procedure TForm6.Button3Click(Sender: TObject);
begin
  FWuppdi.Consume(Label1);
end;

procedure TForm6.FormCreate(Sender: TObject);
begin
  FWuppdi := TWuppdi.Create;
end;

procedure TForm6.FormDestroy(Sender: TObject);
begin
  FWuppdi.Free;
end;

end.
Detlef
"Ich habe Angst vor dem Tag, an dem die Technologie unsere menschlichen Interaktionen übertrumpft. Die Welt wird eine Generation von Idioten bekommen." (Albert Einstein)
Dieser Tag ist längst gekommen
  Mit Zitat antworten Zitat
Benutzerbild von Uwe Raabe
Uwe Raabe

Registriert seit: 20. Jan 2006
Ort: Lübbecke
11.652 Beiträge
 
Delphi 12 Athens
 
#10

AW: Tutorial Interfaces

  Alt 12. Apr 2017, 14:11
Und was meinst du hier mit "Hierarchie einhalten"? Ich muss von der abstrakten Basisklasse ableiten - genauso wie du das interface implementieren musst. Das gibt sich doch eigentlich nichts.
Das Interface kann ich aber in verschiedenen Klassen implementieren, die eine beliebige Vererbungs-Hierarchie haben können und im Extremfall lediglich TObject als gemeinsamen Vorfahren haben.

Ich implementiere schon mal gern Interfaces in Forms, Frames oder Datenmodulen (z.B. ILogger-Implementierungen, die Log-Meldungen in Memos oder Datenbanken schreiben). Das wäre mit abstrakten Klassen gar nicht möglich.

Das Problem mit solchen Tutorials ist halt immer, daß es einfach gehalten sein soll, damit man das dahinter stehende Prinzip versteht. In der Regel gibt es bei solch einfachen Fällen dann natürlich auch valide alternative Lösungen. Ein Tutorial ist eben kein Beispiel für eine Killer-Anwendung.
Uwe Raabe
Certified Delphi Master Developer
Embarcadero MVP
Blog: The Art of Delphi Programming
  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 03:26 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-2025 by Thomas Breitkreuz