Delphi-PRAXiS
Seite 1 von 2  1 2   

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Tutorials und Kurse (https://www.delphipraxis.net/36-tutorials-und-kurse/)
-   -   Tutorial Interfaces (https://www.delphipraxis.net/192364-tutorial-interfaces.html)

Fritzew 12. Apr 2017 11:25


Tutorial Interfaces
 
Liste der Anhänge anzeigen (Anzahl: 1)
Tutorial Interfaces

Warum dieses Tutorial?

Es ist mir aufgefallen das immer wieder Missversändnisse und Unklarheiten über den Nutzen und
Einsatz von Interfaces auftreten.
Deshalb habe ich mich Entschlossen das etwas aufzuhellen.
Dieses Tutorial beinhaltet natürlich meine Sicht darauf und ich lasse mich gerne verbessern.


Was ist ein Interface?

Ein Interface, übersetzt Schnittstelle, ist genau das was der Name uns mitteilt, es ist eine Schnittstelle zwischen verschiedenen
Programmteilen. Der Vorteil liegt in der Trennung von Definition und Implementation.
Ein CodeTeil der eine Schnittstelle benutzt muss nicht wissen wie diese Umgesetzt (Implementiert) ist.

Betrachten wir es wie eine USB Verbindung. Wir haben das Interface (den Stecker, die Buchse) und da können die verschiedensten
Geräte verbunden werden. Alles was jedes Ende wissen muss, ist wie über die Schnittstelle (Interface) kommuniziert wird.
Ob da nun ein Drucker, eine Kamera oder was auch immer daran hängt ist erst einmal unwichtig.

In diesem Tutorial gehe ich erst einmal auf eine recht einfache Anwendung von Interfaces ein.

Ich denke jeder kennt das Problem: Programmeinstellungen, Anwendereinstellunge etc. müssen zwischengespeichert werden, damit der
Anwender seine Einstellungen etc. bei jedem Programmstart wieder findet.

Die Lösung die fast jeder da gerne zumindest am Anfang verwendet sind Inifiles. Einfach zu benutzen und effektiv.
Irgendwann dann sollen die Einstellungen in eine Datenbank oder ähnliches geschrieben werden.
Es steht ein grösserer Umbau an.

Die Lösung? Ja genau Interfaces!

Das TCustomIniFile hat eigentlich schon alles was wir brauchen. Wir könnten einfach ein neue Klasse ableiten und diese benutzen.
Einfacher wird es aber wenn wir daraus den benötigten Teil in ein Interface auslagern und in unseren CodeTeilen dieses benutzen.

Das sieht dann z.B so aus:

Delphi-Quellcode:
   iConfigReadWriter = interface
      ['{AE9CC6E5-F0B8-4D39-8F6C-799423C60A37}']
      function SectionExists(const Section: string): Boolean;
      function ReadString(const Section, Ident, Default: string): string;
      procedure WriteString(const Section, Ident, Value: string);
      function ReadInteger(const Section, Ident: string; Default: Longint): Longint;
      procedure WriteInteger(const Section, Ident: string; Value: Longint);
      function ReadBool(const Section, Ident: string; Default: Boolean): Boolean;
      procedure WriteBool(const Section, Ident: string; Value: Boolean);
      function ReadBinaryStream(const Section, Name: string; Value: TStream): Integer;
      function ReadDate(const Section, Name: string; Default: TDateTime): TDateTime;
      function ReadDateTime(const Section, Name: string; Default: TDateTime): TDateTime;
      function ReadFloat(const Section, Name: string; Default: Double): Double;
      function ReadTime(const Section, Name: string; Default: TDateTime): TDateTime;
      procedure WriteBinaryStream(const Section, Name: string; Value: TStream);
      procedure WriteDate(const Section, Name: string; Value: TDateTime);
      procedure WriteDateTime(const Section, Name: string; Value: TDateTime);
      procedure WriteFloat(const Section, Name: string; Value: Double);
      procedure WriteTime(const Section, Name: string; Value: TDateTime);
      procedure ReadSection(const Section: string; Strings: TStrings);
      procedure ReadSections(Strings: TStrings); overload;
      procedure ReadSections(const Section: string; Strings: TStrings); overload;
      procedure ReadSubSections(const Section: string; Strings: TStrings; Recurse: Boolean = False);
      procedure ReadSectionValues(const Section: string; Strings: TStrings);
      procedure EraseSection(const Section: string);
      procedure DeleteKey(const Section, Ident: string);
      function ValueExists(const Section, Ident: string): Boolean;
   end;
Anstatt einer Methode unserer Klassen mit der Signatur

Delphi-Quellcode:
procedure SaveConfig(Value : TcustomIniFile);
können wir die Signatur ändern zu

Delphi-Quellcode:
procedure SaveConfig(Value : iConfigReadWriter);

für unseren restlichen Code ändert sich nichts da die Signaturen der Methoden gleich sind.

Delphi-Quellcode:
Value.WriteString('DATA','DUMP','Meine Daten');
Dies erfordert natürlich das wir die Erzeugung und Freigebe unserer "Schnittstelle" vom eigentlichen Code trennen.

Also anstatt einer methode:

Delphi-Quellcode:
procedure tuseini.BadSaveData;
var
   lini: TCustomIniFile;
begin
   lini := TIniFile.Create('Was auch immer');
   try
      lini.WriteString('Data', 'Dump', 'meine Daten');
  // etc
   finally
      lini.free;
   end;
end;
haben wir zumindest:

Delphi-Quellcode:
procedure tuseini.BetterSaveSettings(Writer: TCustomIniFile);
begin
   Writer.WriteString('Data', 'Dump', 'meine Daten');
end;

procedure tuseini.BetterSaveData;
var
   lini: TCustomIniFile;
begin
   lini := TIniFile.Create('Was auch immer');
   try
     BetterSaveSettings(lini);
   finally
      lini.free;
   end;
end;

Aus meiner Sicht ist dies die optimale Lösung:

Delphi-Quellcode:
procedure tuseini.SaveSettings(Writer: iConfigReadWriter);
begin
  Writer.WriteString('Test', 'Dummy', 'Default');
end;

procedure tuseini.ReadSettings(Reader: iConfigReadWriter);
begin
 fmySetting := Reader.ReadString('Test', 'Dummy', 'Empty');
end;
Unser Klasse muss nur das Interface kennen, was dahinter wirklich passiert ist nicht wichtig für die Benutzung.

Im Testprojekt ist jetzt auch ein Datamodul dass dieses Interface implementiert.
Es sind nicht alle Methoden befüllt sollte aber den zeigen wie man so einfach die Implementation ändern kann, ohne in der
benutzenden Klasse etwas zu ändern.

Delphi-Quellcode:
  TDataModule1 = class(TDataModule, iConfigReadWriter)
    FDConnection1: TFDConnection;
    FDGUIxWaitCursor1: TFDGUIxWaitCursor;
    FDPhysSQLiteDriverLink1: TFDPhysSQLiteDriverLink;
    procedure DataModuleDestroy(Sender: TObject);
    procedure DataModuleCreate(Sender: TObject);
  private
    procedure DeleteKey(const Section, Ident: string);
    procedure EraseSection(const Section: string);
    function ReadBinaryStream(const Section, Name: string; Value: TStream): Integer;
    function ReadBool(const Section, Ident: string; Default: Boolean): Boolean;
    function ReadDate(const Section, Name: string; Default: TDateTime): TDateTime;
    function ReadDateTime(const Section, Name: string; Default: TDateTime): TDateTime;
    function ReadFloat(const Section, Name: string; Default: Double): Double;
    function ReadInteger(const Section, Ident: string; Default: Longint): Longint;
    procedure ReadSection(const Section: string; Strings: TStrings);
    procedure ReadSections(const Section: string; Strings: TStrings); overload;
    procedure ReadSections(Strings: TStrings); overload;
    procedure ReadSectionValues(const Section: string; Strings: TStrings);
    function ReadString(const Section, Ident, Default: string): string;
    procedure ReadSubSections(const Section: string; Strings: TStrings; Recurse: Boolean = False);
    function ReadTime(const Section, Name: string; Default: TDateTime): TDateTime;
    function SectionExists(const Section: string): Boolean;
    function ValueExists(const Section, Ident: string): Boolean;
    procedure WriteBinaryStream(const Section, Name: string; Value: TStream);
    procedure WriteBool(const Section, Ident: string; Value: Boolean);
    procedure WriteDate(const Section, Name: string; Value: TDateTime);
    procedure WriteDateTime(const Section, Name: string; Value: TDateTime);
    procedure WriteFloat(const Section, Name: string; Value: Double);
    procedure WriteInteger(const Section, Ident: string; Value: Longint);
    procedure WriteString(const Section, Ident, Value: string);
    procedure WriteTime(const Section, Name: string; Value: TDateTime);
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  end;


Dies habe ich im testProjekt über eine simple Factory gelöst.

Im Anhang findet Ihr ein Projekt das dies Umsetzt.

Bei Interesse kann ich auch noch weiter in die Interface problematik einsteigen, Supports, Delegeation etc..

Habe das Tutorial.zip erweitert mit einer DB Lösung

a.def 12. Apr 2017 11:30

AW: Tutorial Interfaces
 
Ich hoffe meine Frage ist eine berechtigte Frage:
worin liegt der Vorteil gegenüber normalen IniFiles in der fertigen ausführbaren Datei?

Delphi-Laie 12. Apr 2017 11:38

AW: Tutorial Interfaces
 
"Ein Interface, übersetzt Schnittstelle, ist genau das was der Name uns mitteilt, es ist eine Schnittstelle zwischen verschiedenen
Programmteilen. Der Vorteil liegt in der Trennung von Definition und Implementation."

Nun, diese Unterscheidung zwischen Deklaration und Implementation wird auch im Unit-Konzept realisiert.
Sogar die übergebenen Variablen bei Unterprogrammen erfüllen dieses Schnittstellenkonzept.

Mithin ist mir der Vorteil der Interfaces leider nicht klargeworden.

DeddyH 12. Apr 2017 11:58

AW: Tutorial Interfaces
 
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;

Fritzew 12. Apr 2017 13:14

AW: Tutorial Interfaces
 
Hallo zusammen, ich habe das Beispiel noch mit einer DB Lösung erweitert. Vielleicht wird es dann besser zu verstehen

Bbommel 12. Apr 2017 13:39

AW: Tutorial Interfaces
 
Zitat:

Zitat von DeddyH (Beitrag 1367309)
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.

DeddyH 12. Apr 2017 14:32

AW: Tutorial Interfaces
 
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.

Bbommel 12. Apr 2017 14:44

AW: Tutorial Interfaces
 
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.

DeddyH 12. Apr 2017 14:49

AW: Tutorial Interfaces
 
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.

ISurf 12. Apr 2017 14:59

AW: Tutorial Interfaces
 
Zitat:

Zitat von Bbommel (Beitrag 1367339)
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.

DeddyH 12. Apr 2017 15:02

AW: Tutorial Interfaces
 
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.

Bbommel 12. Apr 2017 15:05

AW: Tutorial Interfaces
 
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.

Uwe Raabe 12. Apr 2017 15:11

AW: Tutorial Interfaces
 
Zitat:

Zitat von Bbommel (Beitrag 1367339)
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.

a.def 12. Apr 2017 15:12

AW: Tutorial Interfaces
 
Zitat:

(z.B. ILogger-Implementierungen, die Log-Meldungen in Memos oder Datenbanken schreiben).
Und für so eine einfache Sache braucht man ein kompliziertes Interface?

DeddyH 12. Apr 2017 15:19

AW: Tutorial Interfaces
 
Was soll an einem ILogger-Interface kompliziert sein? Da genügt im einfachsten Fall eine Log-Methode mit einem String-Parameter.

Bbommel 12. Apr 2017 15:22

AW: Tutorial Interfaces
 
Zitat:

Zitat von Uwe Raabe (Beitrag 1367348)
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.

Joa, daran wird es wohl liegen. Die meisten Sachen, die ich dazu gelesen habe, bauten die Beispiele tatsächlich immer recht einfach auf - ist ja auch klar, um die Syntax und das Grundprinzip zu lernen -, waren dadurch aber eigentlich auch immer mit den abstrakten Klassen "kompatibel".

Insofern hat mich die Diskussion, die sich jetzt hier daraus ergeben hat, tatsächlich weitergebracht. Mal sacken lassen und mal schauen, wann ich demnächst auf ein Problem stoße, wo ich das dann auch mal anwenden kann. Danke euch allen für die Anregungen!

a.def 12. Apr 2017 15:24

AW: Tutorial Interfaces
 
Das geht doch auch viel einfacher ohne ein Interface. Eine einfache Klasse reicht.
Interfaces sind einfach nur komplizierter Spökes den man erst wieder umständlich erlernen muss :P Ich selber komme mit normalen Klassen wunderbar klar und habe noch keine Funktionalität vermisst.

DeddyH 12. Apr 2017 15:27

AW: Tutorial Interfaces
 
Wo ist denn nun der Unterschied zwischen TDings und IDings? Ist I komplizierter als T?

EWeiss 12. Apr 2017 15:30

AW: Tutorial Interfaces
 
Zitat:

Zitat von DeddyH (Beitrag 1367356)
Wo ist denn nun der Unterschied zwischen TDings und IDings? Ist I komplizierter als T?

Nein aber das eine Dings hat ein T und das andere Dings ein I von daher doch etwas kompliziert. LOL ;)
Aber davon ab Interface in DLL's sind eine feine Sache.

Ausgenomen davon wenn da nicht so viele Getter und Setter wären die im Interface mit übernommen werden müssen.

gruss

stahli 12. Apr 2017 15:33

AW: Tutorial Interfaces
 
Eigentlich hatte ich meinen Beitrag vorhin verworfen, aber vielleicht hilft er ja doch noch wem weiter ->

Vielleicht darf ich gleich nochmal auf mein Tutorial verlinken: http://www.delphipraxis.net/183702-i...-factorys.html

Neben der schon erwähnten möglichen Referenzzählung (wenn man es braucht und will) arbeitet man bei Interfaces eher mit Funktionalitäten statt mit konkreten Klassen.

Jedes Objekt kann mehrere Interfaces unterstützen, was bei Verwendung von Basisklassen in Delphi nicht geht.

Ich kann also alle Objekte bewegen, die IMove unterstützen und alle Objekte Loggen, die ILogging unterstützen.
Die konkrete Klasse spielt in dem Moment keine Rolle mehr. Jede im Projekt verwendete Klasse kann kein, eins oder beide Interfaces unterstützen.

Wenn man ohne Interfaces auskommt spricht da überhaupt nichts dagegen.
Der Einsatz von Interfaces erzeugt auch durchaus einigen Mehraufwand und Umstellung beim Umgang mit Objekten.

Bei komplexen Projekten kann sich der aber lohnen, da man u.U. mehr Struktur in das Projekt bekommen und ggf. Klassen später auch leichter austauschen kann. Man ist dann halt nicht von einer bestimmten Basisklasse abhängig.

DeddyH 12. Apr 2017 15:42

AW: Tutorial Interfaces
 
So isses. Immer, wenn es um maximale Flexibilität geht, sind Interfaces das probate Mittel dazu. Für jedes Fitzchen aber nur noch auf Interfaces zu setzen ist in meinen Augen sinnlos. Wie immer hängt es halt vom Einzelfall ab.

a.def 12. Apr 2017 16:32

AW: Tutorial Interfaces
 
Zitat:

Wo ist denn nun der Unterschied zwischen TDings und IDings? Ist I komplizierter als T?
Für doof halten darf man mich aber auch nicht.

EWeiss 12. Apr 2017 16:37

AW: Tutorial Interfaces
 
Zitat:

Zitat von a.def (Beitrag 1367374)
Zitat:

Wo ist denn nun der Unterschied zwischen TDings und IDings? Ist I komplizierter als T?
Für doof halten darf man mich aber auch nicht.

Er wollte dir doch nur aufzeigen das es im Grunde genommen Fast das gleiche ist.
Denke nicht das er das böse gemeint hat.

gruss

a.def 12. Apr 2017 16:41

AW: Tutorial Interfaces
 
Zitat:

Denke nicht das er das böse gemeint hat.
War es bestimmt auch nicht.

Aber ich finde, dass Interfaces ganz und gar nicht das gleiche sind wie normale Klassen. ich finde normale Klassen wesentlich angenehmer zu schreiben.
Wenn man X Objekte von Y Objekten ableiten muss, macht man im Grundaufbau irgendetwas falsch wie ich finde. Da helfen auch keine Interfaces.

EWeiss 12. Apr 2017 16:48

AW: Tutorial Interfaces
 
Kommt halt darauf an für was man es verwendet.

Wenn ich eine Funktion aus meiner DLL öffentlich machen will dann verwende ich Interface weil der
Anwender sich dann nicht mehr um das laden der DLL kümmern muss.
Zudem erspart es mir für eine Funktion mehrere Exports zu generieren die ich so mit einer Erledigen kann.

gruss

hoika 12. Apr 2017 16:59

AW: Tutorial Interfaces
 
Hallo,
wenn ich bestehende Klassen, die nicht von einer Basisklasse abgeleitet sind,
um eine gemeinsame Funktionalität erweitern will,
geht das nur über Interfaces.

stahli 12. Apr 2017 17:01

AW: Tutorial Interfaces
 
Das Beispiel in #11 von DeddyH zeigt das eigentlich sehr schön.

Ohne Interfaces müsste man Wuppdi so schreiben:

Delphi-Quellcode:
{ TWuppdi }

procedure TWuppdi.Consume(const O: TObject);
begin
  if (O is TEdit) then
   ShowMessage((O as TEdit).DisplayString);
  if (O is TLabel) then
   ShowMessage((O as TLabel).DisplayString);
  if (O is TComboBox) then
   ShowMessage((O as TComboBox).DisplayString);
end;
Man muss also an der Stelle alle Klassen konkret kennen und darauf casten.

Wenn man irgendwann 2 neue Klassen dort anzeigen möchte, muss man diese in der Prozedur nachträglich mit aufnehmen - und in allen anderen Prozeduren, die diese neuen Klassen kennen müssen.

Auf eine gemeinsame Basisklasse zurückzugreifen geht hier ja nicht.

Mit Interfaces ist es halt einfacher, zu prüfen, ob das vorliegende Objekt von der Prozedur bearbeitet werden kann.
Man schaut sich nur noch an, ob das, was ich da habe displayed werden kann oder nicht. Man muss dann nicht mehr wissen, welche Klasse man konkret vorliegen hat und es kann sogar sein, dass man die konkrete Klasse überhaupt nicht kennt.

Wie gesagt, man MUSS das natürlich nicht verwenden, aber es ist gut, wenn man es kennt und darauf zurück greifen kann, wenn man mit einfachen Klassen mal nicht so gut zum Ziel kommt.

Fritzew 12. Apr 2017 17:16

AW: Tutorial Interfaces
 
Der grösste Vorteil von Interfaces ist das die Consumer Klasse gar nichts von der Implementation mitbekommt.
Keinerlei Abhängigkeiten zu anderen Klassen. Jeder der versucht Unit Tests zu schreiben und das auch konsequent :oops: durchzieht
will die Vorteile nicht mehr missen. Die einzige Abhängigkeit ist die Interface Unit, fertig.

Ich arbeite in der CAD-Branche, wir müssen import und export zu verschiedenen Formaten bereithalten.

Unser Klassen haben einfach Methoden die ein Interface entgegennehmen. Welches Format da dann wirklich dahintersteht is egal.
Die verschiedenen Formate werden einfach bei einer Factory registriert.

Delphi-Quellcode:
function readImport(Import : IImport3d) : boolean;
Ob da jetzt ein Step, SAT, Igel oder was auch immer ankommt ist egal.
Die einzelnen Bereiche können unabhängig voneinander getestet werden und gut ist..

Uwe Raabe 12. Apr 2017 17:39

AW: Tutorial Interfaces
 
Ich verweise da mal schamlos auf meine Reihe von Blog-Artikeln von 2010 zum Visitor Pattern: The Visitor Pattern – Part 1, Part 2, Part 3 und Part 4

Insbesondere Part 2 und 3 gehen auf die Vorteile bei der Verwendung von Interfaces ein. Ist aber auch schon etwas mehr zu lesen...

Zacherl 12. Apr 2017 19:21

AW: Tutorial Interfaces
 
Was ich mich schon länger frage: Gibt es eigentlich einen bestimmten Grund, warum Interfaces in Delphi keine privaten Methoden haben dürfen? Unter C++ ist das beispielsweise problemlos möglich.

hoika 12. Apr 2017 19:25

AW: Tutorial Interfaces
 
Hm?
wieso privat?
Die Methoden müssen doch implementiert werden,
wenn sie privat wären, macht das doch keinen Sinn, oder?

Fritzew 12. Apr 2017 19:37

AW: Tutorial Interfaces
 
Zitat:

wieso privat?
Die Methoden müssen doch implementiert werden,
wenn sie privat wären, macht das doch keinen Sinn, oder?
Interface Methoden sind immer aufrufbar, egal wo sie definiert sind.
Ich mache das immer so, dann lassen Sich die Funktionen nur über das Interface ansprechen, verhindert einfach dass man aus versehen Klassen-Instanzen und Interfaces mixed.
Als IInterface geht es als Tclass nicht

Zacherl 12. Apr 2017 20:23

AW: Tutorial Interfaces
 
Zitat:

Zitat von hoika (Beitrag 1367408)
wieso privat?
Die Methoden müssen doch implementiert werden,
wenn sie privat wären, macht das doch keinen Sinn, oder?

Naja protected meinetwegen, um bei Klassen-Terminologie zu bleiben.

Zitat:

Zitat von Fritzew (Beitrag 1367409)
Interface Methoden sind immer aufrufbar, egal wo sie definiert sind.
Ich mache das immer so, dann lassen Sich die Funktionen nur über das Interface ansprechen, verhindert einfach dass man aus versehen Klassen-Instanzen und Interfaces mixed.
Als IInterface geht es als Tclass nicht

Hintergrund meiner Frage ist, dass ich Interfaces sehr gerne zum Vermeiden von zirkulären Unit-Referenzen bzw. "Super-Units" benutzen würde. Habe ab und zu mehrere Klassen, die sich "kennen" müssen und untereinander auf interne Methoden zugreifen. Öffentlich will ich diese Methoden nicht deklarieren, weil ein Zugriff von außen unerwünscht ist.

Stevie 12. Apr 2017 21:53

AW: Tutorial Interfaces
 
Zitat:

Zitat von Zacherl (Beitrag 1367407)
Was ich mich schon länger frage: Gibt es eigentlich einen bestimmten Grund, warum Interfaces in Delphi keine privaten Methoden haben dürfen? Unter C++ ist das beispielsweise problemlos möglich.

Weil C++ keine Interfaces hat, sondern das alles (abstrakte) Klassen sind? Und Klassen haben nunmal Sichtbarkeiten.

Zacherl 12. Apr 2017 22:03

AW: Tutorial Interfaces
 
Zitat:

Zitat von Stevie (Beitrag 1367431)
Zitat:

Zitat von Zacherl (Beitrag 1367407)
Was ich mich schon länger frage: Gibt es eigentlich einen bestimmten Grund, warum Interfaces in Delphi keine privaten Methoden haben dürfen? Unter C++ ist das beispielsweise problemlos möglich.

Weil C++ keine Interfaces hat, sondern das alles (abstrakte) Klassen sind? Und Klassen haben nunmal Sichtbarkeiten.

Das weiß ich, aber dank multiple-inheritance ist es ja dennoch 1 zu 1 das Gleiche. Mit dem C++ Vergleich wollte ich aber auch nur ausdrücken, dass es technisch auf jeden Fall möglich ist und ich mich deshalb wundere, dass Delphi hier eine Einschränkung vorgibt.

Uwe Raabe 12. Apr 2017 22:22

AW: Tutorial Interfaces
 
Zitat:

Zitat von Zacherl (Beitrag 1367419)
Hintergrund meiner Frage ist, dass ich Interfaces sehr gerne zum Vermeiden von zirkulären Unit-Referenzen bzw. "Super-Units" benutzen würde. Habe ab und zu mehrere Klassen, die sich "kennen" müssen und untereinander auf interne Methoden zugreifen. Öffentlich will ich diese Methoden nicht deklarieren, weil ein Zugriff von außen unerwünscht ist.

Dann deklariere die Interfaces in zwei Units: eine mit den öffentlichen und eine mit den protected. Streng genommen sind protected Methoden bei Klassen auch keine saubere Lösung für eine solche Trennung (was ja durch ein strict protected auch forciert werden kann). Insofern ist das eh nur eine Konvention und die kannst du auf uses-Ebene ebenso gut (oder schlecht) durchsetzen.

Zacherl 12. Apr 2017 23:26

AW: Tutorial Interfaces
 
Zitat:

Zitat von Uwe Raabe (Beitrag 1367434)
Zitat:

Zitat von Zacherl (Beitrag 1367419)
Hintergrund meiner Frage ist, dass ich Interfaces sehr gerne zum Vermeiden von zirkulären Unit-Referenzen bzw. "Super-Units" benutzen würde. Habe ab und zu mehrere Klassen, die sich "kennen" müssen und untereinander auf interne Methoden zugreifen. Öffentlich will ich diese Methoden nicht deklarieren, weil ein Zugriff von außen unerwünscht ist.

Dann deklariere die Interfaces in zwei Units: eine mit den öffentlichen und eine mit den protected. Streng genommen sind protected Methoden bei Klassen auch keine saubere Lösung für eine solche Trennung (was ja durch ein strict protected auch forciert werden kann). Insofern ist das eh nur eine Konvention und die kannst du auf uses-Ebene ebenso gut (oder schlecht) durchsetzen.

Ja, vielleicht bin ich da einfach zu penibel. Ich gehöre auch tatsächlich zu den ca. 10 Leuten weltweit, die konsequent
Delphi-Quellcode:
strict private
verwenden.

Stevie 13. Apr 2017 01:04

AW: Tutorial Interfaces
 
Zitat:

Zitat von Zacherl (Beitrag 1367439)
Ja, vielleicht bin ich da einfach zu penibel. Ich gehöre auch tatsächlich zu den ca. 10 Leuten weltweit, die konsequent
Delphi-Quellcode:
strict private
verwenden.

Wenn die Class completion da nich immer drauf schei*** würde, wär ich der elfte.

delphirocks 14. Apr 2017 22:15

AW: Tutorial Interfaces
 
Interfaces haben in C++ normalerweise auch keine privaten Member, sonder sind als public pure abstrakte Methoden implementiert.

Ich denke da gibt es keinen grossen Unterschied zu Delphi:
Code:
struct IWriter {  // structs immer public
   virtual void Write() = 0;
};

class FileWriter : public IWriter {
public:
   void Write() override { doTheWriting(); }
};

hph97 10. Okt 2017 10:05

AW: Tutorial Interfaces
 
Warum funktioniert das Beispiel von DeddyH nur, wenn alles in der selben Unit implementiert ist? Wenn ich diesen kompletten Interface Teil in einer Unit2 auslagere so bekomme ich die Compiler Fehlermeldung "E2010 Inkompatible Typen IDisplay und TEdit".


Alle Zeitangaben in WEZ +1. Es ist jetzt 13:16 Uhr.
Seite 1 von 2  1 2   

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