Delphi-PRAXiS

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 13:59

Schon wieder: Warum Interfaces II
 
Dies ist die zweite Staffel eines Themas von 2016, das - wie üblich - zum Schluss völlig wegmäanderte.

Wie der TE damals weiß auch ich so halbwegs, was Interfaces sind, wie man sie konstruiert und wozu sie gut sein sollen. Ich weiß auch, dass die Beispiele, wozu Interfaces nützlich sind, aus Demonstrationsgründen meist so einfach gestrickt sind, dass man auch gut ohne sie klarkäme. Wie der TE höre ich immer feurige Beteuerungen, dass es ohne Interfaces nicht geht (vor allem, wenn man auf Nick Hodges hört), und wie er tue ich mich schwer, für mein eigenes Programm eine Stelle zu finden, wo Interfaces eine Verbesserung wären.

Ich stelle gerade von AsyncCalls von Andreas Hausladen auf Omnithread von Primož Gabrijelčič um. Beide benutzen natürlich heftigst Interfaces. Und insbesondere anhand des sehr überschaubaren Quelltextes von AsyncCalls fände ich eine Darlegung lehrreich, warum Interfaces und ob es auch ohne sie ginge. Denn hier greift doch das häufige Argument "Die spätere Implementierung ist zurzeit noch nicht bekannt und man hält sich alles offen" nicht. Und Gabrijelčič schreibt an irgendeiner Stelle (die ich gerade nicht mehr finde) sinngemäß: Man kann das Interface nehmen oder auch die Klasse, wenn man das bevorzugt. Na, wenn das so ist, denkt man sich, warum nicht gleich nur Klassen?

himitsu 22. Jun 2020 14:25

AW: Schon wieder: Warum Interfaces II
 
JETZT geht es oft auch ohne sie, nachdem endlich, nach Jahrzehnten des Wartens, die Records erweitert wurden.

Im Grunde werden hier Interfaces "oft" nur dafür genutzt, damit Objekte automatisch freigegeben werden.
In den NextGen-Compilern ala Android und iOS, gab es mit dem ARC auch dafür schon blöde eine Lösung, da ALLE Delphi-Objekte wie Interfaces behandelt wurden,
und auch jetzt kann man mit den neuen Managed Records sowas überall im Delphi nutzen, also nun auch im Windows.


Persönlich hab ich oft versucht ohne Interfaces auszukommen, vor allem um die doppelte Deklaration und zusätzliche Zwischenebene loszuwerden.

Benmik 22. Jun 2020 14:41

AW: Schon wieder: Warum Interfaces II
 
Zitat:

Zitat von himitsu (Beitrag 1468013)
Persönlich hab ich oft versucht ohne Interfaces auszukommen, vor allem um die doppelte Deklaration und zusätzliche Zwischenebene loszuwerden.

Das ist es ja gerade, alles in den Interfaces muss ja in jedem Fall implementiert werden, also was bringt (hier) die Doppelarbeit?
Ja, die Referenzzählung. Aber das mache ich lieber selbst und das stört mich nicht. Bei der Referenzzählung gibt es auch Fallen, und dann kann man das gleich selbst erledigen.

Was das mit Managed Records zu tun hat, habe ich nicht verstanden. Warum nicht Klasse und gut ist?

DeddyH 22. Jun 2020 14:57

AW: Schon wieder: Warum Interfaces II
 
Auch wenn viele Delphi-Entwickler das zu glauben scheinen ist der primäre Zweck von Interfaces nicht die Referenzzählung, sondern die Abstraktion. Einer Klasse oder Routine, die ein bestimmtes Interface erwartet, sind die konkrete Implementation und der konkrete Typ völlig egal. Das bedeutet, dass für die Nutzung auch keine bestimmte Klassenhierarchie eingehalten werden muss, es kann jede beliebige Objektinstanz übergeben werden, sie muss lediglich das Interface implementieren.

stahli 22. Jun 2020 14:59

AW: Schon wieder: Warum Interfaces II
 
Die nicht notwendige Freigabe ist ein (ggf.) positiver Effekt von Interfaces.
Der Nachteil ist die doppelte Deklaration (den möchte ich mit meinem UnitOptimizer abstellen, so dass das automatisiert gemacht wird).

Managed Records müssen nicht freigegeben werden. Wenn es nur um das Unterlassen der Freigabe geht, kann man jetzt diese benutzen.
Dann hat man allerdings nicht so etwas wie Vererbung.

Ein zweiter Wesentlicher Vorteil von Interfaces ist die Zuweisung von mehreren Interfaces zu einer Klasse.
Man kann also "Fähigkeiten" definieren, die von unterschiedlichen Klassen erfüllt werden können.
Alles, was "CanFly" unterstützt, kann man dann fliegen lassen. Egal ob Vogel, Biene oder Flugzeug.

Diese Option hat man eben nur mit Interfaces.

Ein Nachteil ist, dass sie deutlich langsamer sind und mehr Speicher brauchen als reine Objekte und Records.

freimatz 22. Jun 2020 15:09

AW: Schon wieder: Warum Interfaces II
 
Zitat:

Zitat von Benmik (Beitrag 1468011)
Dies ist die zweite Staffel eines Themas von 2016, das - wie üblich - zum Schluss völlig wegmäanderte.

Und inwiefern hat das nicht die Frage beantwortet?

Ich bin mal böse: Umgekehrt, braucht keine interfaces wenn man Delphi nur dazu benutzt um Programme zusammenzubasteln. :twisted:

himitsu 22. Jun 2020 15:14

AW: Schon wieder: Warum Interfaces II
 
@DeddyH: Ja, der primäre Zweck vom Erfinder, aber nicht der primäre Zweck warum es oft eingesetzt wird, denn da ist es oft nur die Referenzzählung,
und da kann man auch gut ohne auskommen.

Also wenn das Interface nur innerhalb einer Komponente steckt, nur der Referenzzählung dient und wo der Unterbau auch nicht austauschbar ist.


Zitat:

Diese Option hat man eben nur mit Interfaces.
Oder mit Objekten, wo der Vorfahre fliegen kann und durch überschreiben (override) verschiedene Ableitungen das dann anpasssen.
TStrings/TDataSet/TCollection/...

Interface: erst wird der Vogel erfunden und anschließend das Fliegen als Gemeinsamkeit zwischen den verschiedenen Tieren/Maschienen dann geteilt
Vererbung: erst erfindet man das Fliegen und baut daraus dann z.B. einen Vogel oder ein Flugzeug

Nur bei Mehrfachvererbung und bei nachträglichen Erweiterungen hapert es dann etwas: TLebewesen -> TLaufen -> TFliegen -> TVogel oder TFlugzeug :?:

Lemmy 22. Jun 2020 15:15

AW: Schon wieder: Warum Interfaces II
 
und um nochmal auf das Thema zu kommen:

Zitat:

Zitat von freimatz (Beitrag 1468018)
Zitat:

Zitat von Benmik (Beitrag 1468011)
Dies ist die zweite Staffel eines Themas von 2016, das - wie üblich - zum Schluss völlig wegmäanderte.

Und inwiefern hat das nicht die Frage beantwortet?


Benmik 22. Jun 2020 15:29

AW: Schon wieder: Warum Interfaces II
 
Zitat:

Zitat von freimatz (Beitrag 1468018)
Ich bin mal böse: Umgekehrt, man braucht keine Interfaces, wenn man Delphi nur dazu benutzt, um Programme zusammenzubasteln.

Das ist es ja, wie alle anderen möchte ich natürlich lieb :stupid: sein und kein böser Pfuscher, der Programme nur zusammenbastelt.

Bitte nochmal die Intention beachten, derentwegen ich den alten Thread auch nicht ausgegraben und reanimiert, sondern einen neuen eröffnet habe: Meine Bitte war, anhand von anspruchsvollem, realem Code konkret den Nutzen der dort verwendeten Interfaces aufzuzeigen und mir (und anderen) verständlich zu machen.

Um Missverständnisse zu vermeiden: Es ist nicht meine Intention, gegen Interfaces zu stänkern. Ich glaube nicht, dass man sie eingeführt und sich viel Arbeit damit gemacht hätte, wenn sie nicht nützlich wären. Wie viele andere schaffe ich nur nicht den Sprung, für mich eine konkrete Anwendung für sie zu finden. Kann gut sein, mein Code ist dafür nicht anspruchsvoll genug, wäre völlig OK. Ich sehe aber auch bei AsyncCalls den Nutzen nicht, und hier wäre ein kompetenter Fingerzeig einfach sehr willkommen.

DeddyH 22. Jun 2020 15:39

AW: Schon wieder: Warum Interfaces II
 
Nur ein Anwendungsfall von vielen: Class Factory

sakura 22. Jun 2020 15:42

AW: Schon wieder: Warum Interfaces II
 
Zitat:

Zitat von Benmik (Beitrag 1468022)
Meine Bitte war, anhand von anspruchsvollem, realem Code konkret den Nutzen der dort verwendeten Interfaces aufzuzeigen und mir (und anderen) verständlich zu machen.

Das wird eher nicht passieren, da dieser Code meist "anspruchsvoll" ist. Dafür wird sich kaum einer die Zeit nehmen, oder gar Produktivcode posten.
Das Konzept muss man einfach verstehen und anwenden, dann hat man recht schnell selbst den anspruchsvollen Code.

Aber ein Beispiel, Du hast eine Klasse "XYZ", welche Daten eines Typs überprüft - sie kennt deren Struktur und Anforderungen. Diese Klasse (nennen wir sie auch einen Service), weiß nicht, was mit den Daten passiert, oder wo diese herkommen, bzw. gespeichert werden. Das wird durch andere Klassen erledigt. Zugriff auf diese Klassen wird Dir über Interfaces (Schnittstellen) erklärt. Wenn Du Daten lesen/speichern musst, dann holst Du Dir den gerade implementierenden Service heran und mittels der Schnittstelle kannst Du diese Daten laden und speichern. Dieses Klasse XYZ interessiert jetzt aber nicht, ob die Daten aus einer XML Datei kommen, einer Datenbank oder aus dem TKitchenSink. Wozu auch, evtl. willst Du dem Anwender die Auswahl dazu überlassen. Am Ende muss aber jede Implementierung die Methoden zum Laden und Speichern anbieten und fertig ist's.

Und ein anderer Service implementiert die Verarbeitung der Daten (z.B. Druck, Darstellung, ...) und greift auf den Service (implementiert durch XYZ) zu. Dieser Service weiß jetzt, dass die Daten garantiert korrekt sind. Diesen interessiert nicht, wo diese herkommen, oder ob es diese überhaupt gibt. Aber wenn es diese gibt, dann sind die korrekt. Und wenn welche gespeichert werden sollen, dann wird sich XYZ schon darum kümmern, mich zu informieren, ob diese korrekt sind...

Dazu verwendet man i.A. Interfaces. Referenzzählung, autom. Freigabe, etc. sind nette Extras, aber nicht der Grund.

...:cat:...

sakura 22. Jun 2020 15:42

AW: Schon wieder: Warum Interfaces II
 
Ein weiteres Stichwort wäre noch: Dependency Injection
Und: Unit Tests
Inkl. Mocking

...:cat:...

himitsu 22. Jun 2020 15:45

AW: Schon wieder: Warum Interfaces II
 
Es gibt halt ein paar bekannte Dinge, wo sie verwendet werden:

* automatische Speicherverwaltung
gibt sich selbst frei, wenn nicht mehr benutzt

* Austauschbarkeit, siehe sakura
das Interface ist gleich, aber was runter ist, muß es nicht sein und dem Nutzer braucht es nicht zu interessieren
z.B. ein Einstellungenspeicherinterface, was auf INI, CSV, Datenbank, Registrie, Webservice oder ... geht
oder wo drunter je nach Zielsystem für Android/Linux/iOS/Windows/... die Behandlung anders ist

* nachträgliche Verbindung
z.B. das "einheitliche" Verhalten für datensensitive Komponenten zugreifbar machen
TDBEdit und TDBCheckbox sind nicht voneinander vererbt, bzw. bekommen ihre Datenbindung erst am Ende ihrer Vererbungshierarchie

* in Richtung COM-Interfaces (vermutlich der Ursprng der Delphi-Interfaces)
da wird es und Anderem als Schnittstelle zu Objekten benutzt, welche sich außerhalb deines Programms befinden

* ...

Uwe Raabe 22. Jun 2020 16:13

AW: Schon wieder: Warum Interfaces II
 
Zitat:

Zitat von Benmik (Beitrag 1468022)
Meine Bitte war, anhand von anspruchsvollem, realem Code konkret den Nutzen der dort verwendeten Interfaces aufzuzeigen und mir (und anderen) verständlich zu machen.

Das ist in der Tat ein Problem. Selbst wenn jemand seinen anspruchsvollen, realen Code zeigen würde, bedürfe es vermutlich eines gehörigen Aufwands, konkret den Nutzen der dort verwendeten Interfaces aufzuzeigen und dir (und anderen) verständlich zu machen. Manchmal liegt die Rechtfertigung für Interfaces ja nicht mal im lokalen Anwendungsfall, sondern im großen Drumherum. Dazu müsste man ja auch einen Großteil der Architektur des gesamten Programms erklären. Zumindest ich selbst hätte dazu weder die Muße noch die Zeit.

Benmik 22. Jun 2020 16:26

AW: Schon wieder: Warum Interfaces II
 
Zitat:

Zitat von sakura (Beitrag 1468025)
Das wird eher nicht passieren, da dieser Code meist "anspruchsvoll" ist. Dafür wird sich kaum einer die Zeit nehmen, oder gar Produktivcode posten.

Zitat:

Zitat von Uwe Raabe (Beitrag 1468029)
...bedürfe es vermutlich eines gehörigen Aufwands... Zumindest ich selbst hätte dazu weder die Muße noch die Zeit.

Das verstehe ich. Manchmal hat man ja Glück und irgendwen packt der Ehrgeiz. AsyncCalls und Omnithread sind ja auch sehr bekannt und manch einer hat sich den Code schon früher mal angeschaut.

Auf der anderen Seite geht es mir ja auch gar nicht darum, in die Tiefen dieser Units einzusteigen. Ich dachte halt, jemand sieht sich die Interfaces und die Implementierungen an und hat dann ein grobes Bild, unabhängig davon, was die Implementierung dann konkret macht.

Ich kann mir auch vorstellen, dass ein lakonisches Fazit lautet: Kann man mit Interfaces machen, muss man nicht. Sir Rufo beispielsweise stand ja im Ruf, alles mit Interfaces zu machen, was bei Drei nicht auf dem Baum war. Für den Fall, dass jemand sagt: Schau mal, hier ist das Interface X, und da die Implementierungen A, B und C, und als Klasse wäre das nicht so einfach/günstig, dann wäre das für meinen Durchdringungsprozess toll gewesen.

freimatz 22. Jun 2020 16:42

AW: Schon wieder: Warum Interfaces II
 
Zitat:

Zitat von sakura (Beitrag 1468026)
Ein weiteres Stichwort wäre noch: Dependency Injection
Und: Unit Tests
Inkl. Mocking

An die dachte ich gerade auch. Und macht man nicht Dependency Injection wegen unit-tests (nicht zu verwechseln mit dunit / Integrationstest)? Hauptgrund bei mir interfaces einzusetzen ist DI .

Zu Dependency Injection wegen unit tests gibt es im inet tonnenweise - leider kaum was für Delphi. (Da sind leider zu viele Basterler unterwegs.)

Zitat:

Zitat von Benmik (Beitrag 1468030)
Sir Rufo beispielsweise stand ja im Ruf, alles mit Interfaces zu machen, was bei Drei nicht auf dem Baum war.

Bei uns in der Firma bekommen ca. 90% der neuen Klassen ein interface.

QuickAndDirty 22. Jun 2020 17:31

AW: Schon wieder: Warum Interfaces II
 
@Benmik:
Interfaces sind sehr hilfreich, wenn ich Programmteile vor einander Verbergen will.
Wenn ich Programmteile von einander möglichst stark logisch trennen will und zwar so starke das ich den einen Programmteil kompilieren kann ohnde das der andere überhaupt auf der Festplatte liegt.
Sprich IInterface Nachfahren eignen sich zum definieren von Schnittstellen ohne das dabei Typ Abhängigkeiten entstehen.

Beispiel wäre ein klassisches MVC oder MVP Entwurfsmuster.
IVIEW könnte eine Windows-Oberfläche sein oder Userinterface das auf natürlicher Sprache basiert oder ein Proxy.
Wer auch immer meine IVIEW entwickelt es kann mir total egal sein, weil ich einfach nur das Interface bediene.

Es macht sinn!

Ich weiß nicht ob das mit den neuen records auch funktioniert... aber ich habs immer mit interfaces gemacht!

Außerdem braucht man Interfaces für Kompatibilität zu Windows Com-Objekten und natürlich für JNI.

Benmik 22. Jun 2020 17:55

AW: Schon wieder: Warum Interfaces II
 
Zitat:

Zitat von QuickAndDirty (Beitrag 1468035)
Es macht Sinn!

Ich habe ja keinerlei Zweifel, dass es Sinn macht! Nur sind in aller Regel die genannten Anwendungsbereiche außerhalb meiner Einzel-Bastel-Sphäre. AsyncCall und Omnithread sind abgeschlossene, überschaubare Entitäten, die dennoch (?) Interfaces haben, und da habe ich gedacht, anhand dieser - zweifellos professionellen - Codeteile könnte ich dem Verständnis und eventuell sogar einem eigenen Einsatz näher kommen, sofern mir klar wird, dass es etwas für mich bringt. Im Moment ist mir das nicht klar; und ich weiß nicht, ob das daran liegt, dass bei mir Interfaces nicht sinnvoll eingesetzt werden können, oder daran, dass ich nicht sehen kann, wo es sinnvoll wäre.

stahli 22. Jun 2020 18:20

AW: Schon wieder: Warum Interfaces II
 
@Benmik

Das ist eigentlich normal.

Irgendwann wird es "Klick" machen. Ab besten durch die Praktische Verwendung.

Ich stelle Dir einfach mal eine Aufgabe:

Erstelle insgesamt 100 Objekte von insgesamt 5 Klassen (direkt von TObject abgeleitet).
Jede Klasse soll eine Eigenschaft Value haben.
Eine vom Typ Boolean, String, Real usw.
Die 100 Objekte sammelst Du in einer Liste.
Die Liste übergibst Du einer Funktion Logging.
In Logging rufst Du für jedes Objekt dessen Methode Log auf.
In Log gibst Du den Wert aus Value immer als String aus.

Zwei Besonderheiten:
1) Du darfst in Logging nicht die Objekte casten.
2) Du darfst die Objekte nicht freigeben und dennoch keinen Memoryleak erhalten.


Man muss m.E. einfach damit arbeiten, um den Zweck zu verinnerlichen.

Aber man muss es auch nicht erzwingen. Wenn sich Dir der Zweck (noch) nicht erschließt, dann brauchst Du es eben nicht.
Immer wenn man Objekte castet, könnte das ein Indiz für die Sinnhaftigkeit von Interfaces sein.

Man hat letztlich eher eine Funktionalität, mit der man arbeitet. Die Classen spielen dann nicht mehr die Rolle, sondern funktionelle Einheiten.
Im obigen Beispiel ist das die Funktion "Log".

Rollo62 22. Jun 2020 18:36

AW: Schon wieder: Warum Interfaces II
 
Zitat:

Zitat von himitsu (Beitrag 1468027)
Es gibt halb ein paar bekannte Dinge, wo sie verwendet werden:

* automatische Speicherverwaltung
gibt sich selbst frei, wenn nicht mehr benutzt

* Austauschbarkeit, siehe sakura
das Interface ist gleich, aber was runter ist, muß es nicht sein und dem Nutzer braucht es nicht zu interessieren
z.B. ein Einstellungenspeicherinterface, was auf INI, CSV, Datenbank, Registrie, Webservice oder ... geht
oder wo drunter je nach Zielsystem für Android/Linux/iOS/Windows/... die Behandlung anders ist

* nachträgliche Verbindung
z.B. das "einheitliche" Verhalten für datensensitive Komponenten zugreifbar machen
TDBEdit und TDBCheckbox sind nicht voneinander vererbt, bzw. bekommen ihre Datenbindung erst am Ende ihrer Vererbungshierarchie

* in Richtung COM-Interfaces (vermutlich der Ursprng der Delphi-Interfaces)
da wird es und Anderem als Schnittstelle zu Objekten benutzt, welche sich außerhalb deines Programms befinden

* ...

Zusätzlich dazu sehe ich Interfaces hauptsächlich als Vertrag, für die Schnittstelle des Objektes.
Was dahinter vorgeht, geht mich nichts an.

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

Benmik 23. Jun 2020 16:38

AW: Schon wieder: Warum Interfaces II
 
Ja, vielen Dank stahli. "Supports" hatte ich mir schon angesehen. Wenn ich dazu komme, verwirklich ich das Beispiel, aber im Moment ruft das RL.

generic 26. Jun 2020 15:06

AW: Schon wieder: Warum Interfaces II
 
Vor kurzem hatte ich zu Interfaces meine Meinung in Youtube "Coding Bott" geteilt:
https://www.youtube.com/watch?v=S5kX1N2G5hg

Es gibt ein follow up Video, welches sich mit dem reference counting beschäftig.
https://www.youtube.com/watch?v=wrnyJW6dtgY

stahli 26. Jun 2020 15:46

AW: Schon wieder: Warum Interfaces II
 
Schönes Video wieder :thumb:

Statt TInterfacedObject kann man auch von TInterfacedPersistent ableiten, wenn man die automatische Referenzzählung nicht will. Die wird dort einfach abgeschaltet.

MichaelT 26. Jun 2020 15:59

AW: Schon wieder: Warum Interfaces II
 
Super Videos. Sehr gut erklärt.

Zitat:

Zitat von generic (Beitrag 1468261)
Vor kurzem hatte ich zu Interfaces meine Meinung in Youtube "Coding Bott" geteilt:
https://www.youtube.com/watch?v=S5kX1N2G5hg

Es gibt ein follow up Video, welches sich mit dem reference counting beschäftig.
https://www.youtube.com/watch?v=wrnyJW6dtgY


MichaelT 26. Jun 2020 18:15

AW: Schon wieder: Warum Interfaces II
 
Zumal schon die einsichtigen Beispiele kamen eine kleine Anmerkung.

Eine interface ist bis auf seine syntaktischen Einschränkungen mal die Schnittstelle eines Modul, genauso wie die Klasse eines darstellt und als 'Notlösung' die abstrakten.

Anwendungsgebiert: Datengetriebene Anwendungen in einem nicht trivialen Framework zumeist ekzessiv kombiniert mit Entwurfsmustern.

Konkretes Beispiel: Eine Planungs- und Analyseanwendung für Produktions- und Auftragsdaten in Kombination und das mit praktisch nicht merkbarer Zeitverzögerung von naja pro Planungslauf von 0,5 Sekunden.

Das Framework bestand
a) aus einem (Object)Cache und die darin verwendeten Informationsstrukturen bestimmten das Layout in der DB
a1) darauf basierend Code Generierung, wegen der Migration sowohl der DB als auch GUI Frontendtechnologoie (Winforms vs. WPF usw..)
b) ein GUI Framework bei dem die Kontrollelemente von der Rectangle selbst gemalt wurden
c) tonnenweise PiPaPo im Anwendungsframework
d)

zur Zeit von .net 2.0 bis hinauf 3.5 beginnend WPF.

Das Framework war 'Hollywood' pur. Don't call us, we call you. Der Vorteil war, dass die GUI tatsächlich nur sich um die grafischen Aspekte und die Navigation hat gekümmert, aber die Cacheobjekte gewusst haben was mit ihnen getan werden darf und kann. Beispiel: Die Summen haben sich selbst disaggregiert (in Summanden zerlegt) und die GUI hat nurmehr den passenden Farbton bei der Navigation in die Tiefe in der Summen und der Kopfzeile bereitgestellt.

a1) Damit die Fehlerfreiheit gewährleister bleibt muss man bis zum beim Faktorisieren zum äußersten gehen. Im prozeduralen Paradigma faktorisiert man solange bis die Anzahl der Parameter welche an eine Prozedur (auch welche mit Rückgabetyp welche mit Funktionen an sich mal wenig zu tun hätten) wieder wächst. Im OO Paradigma gibt es dasselbe Spiel bei den Methoden und ... das ganze Spiel erstreckt sich dahinter noch auf die Ebene des Moduls.

Der schlagende Vorteil war, dass die Daten gewusst haben wie sich sich Anzeigen und die GUI auf des Anwendungskontexts auch. Damit reduzierte sich die manuelle Codierarbeit auf ca. 350 Zeilen und der Rest für außenstehende Magic. Das Zeug war monstergeil.

Meine Kollegas haben dermaßen brutal mit Entwurfsmustern über die Stränge geschlagen, dass C# Programmierer welche die Wartung übernehmen wollten (Nachteil von generativen Ansätzen) verzweifelt vor dem Schirm zusammenbrachen.

Die Lernkurve ist gewaltig steil. Du bekommst mit der Zeit, wenn du nicht grausam aufpasst Side Effekts. Ein Freelancer investiert nicht 3 Monate oder mehr, auch wenn diese Schätzung eher der Verzweiflung im ersten Moment geschuldet ist um dann möglw. eine Rechnung schreiben zu können über eine paar Stunden. Die Kompromisslösung war der Sohn eines Geschäftsführers welcher sich neben dem Studium hat eingearbeitet.

Eine groß Berliner Softwarehaus mit mehreren tausend Mitarbeitern kommt auf den Level nicht hin. Diese spezielle Anwendung und die damit verbundenen Anforderungen haben das ganze auch notwendig gemacht, denn die Benutzer der Kunden wollen die eigenen Daten sehen. Also braucht man einen relativ schnittigen Weg die in den Cache zu bekommen. Ein Mittelstandsbetrieb (in .at) macht kein Projekt zu beginn. Allein hat sich schon damals gezeigt (vor ca. 15) Jahre, dass OLAP dafür nicht ausreicht resp. nicht ausgereicht hat.

Das Projekt ist für mich eigentlich die Top Anforderung wo das Ausfahren der Entwurfsmuster am Ende tatsächlich auf fruchtbringend ist. Man hängt halt dann am eigenen Framework fest und dann muss man es anwenden. Unsere Jungs haben das nach dem normalen Arbeitstag von ca. 10 Stunden 6 Tage die Woche selbst entwickelt. Trotzdem gelten die zuvor genannten Restriktionen.

-

Mir fiel allein auf, dass bei viele Projekte dieser exzessive Einsatz heute Standard ist und am Ende alle eher die Nerven raubt. Wenn der SAP Hardcore Profi Programmier kommt und Code mit Field Symbols zur Laufzeit generiert. Das ist schon heftig, aber bei dem integrierten Debugger ließ sich der Ablauf noch verfolgen, dem Code hat man nicht angesehen was er tun soll. Das ganz mit OO-ABAP und alle haben geweint. Endlich hatten alle eine Lösung bei der wenn ein Mitarbeiter umfällt, alle anderen auf einen Knopf können drücken und es geht tatsächlich immer. Blöd nur wenn der Berater umfällt.

Da lobe ich mir die Beispiele hier und auch die im Kommentar zuvor angesprochenen Videos. Interfaces ist eher kleinteilig und sehr verspielt. Bei offensichtlichern größeren Brocken ans Software mit klar abgegerenzten Aufgaben die gemeinhin Komponenten (nicht visuell) heißen, wird die Sache klarer.

-

Das einfachste Interface ist ein (packed) Record der die Daten hält und ein anderer der einen Teilaspekt repräsentiert und alles andere wird ausmaskiert. Damit geht der Typecast nicht schief.

Die Verbundstruktur hält immer nur Daten und Module/Dateien die dazu passenden Funktionen. Die Verbindung macht der Typ und wenn man einen untypisierten Pointer hat, dann kann man jeder Funktion alle Daten übergeben. Die Frage ist wie lange die Sache gutgeht.


Das COM und die Interfaces sind ein Nachbau bei dem anderes wie wie einem PC das OS und der Transaktionsmonitor das Modul salopp formuliert in die Session reingejittet haben. Ein Objekt auf der Instanzebene heute entspricht solch einem geladenen Modul.

Einer der schlagenden Vorteile von Delphi ist einfach, man ein Interface Ebene über das Modul/Unit mehr hat. Ansonsten bist du schnell auf der Schiene einmal Interface - alles Interface. Deswegen hat man dann Interfaces überall. C kennt keine Interfaces sondern Files und Compilation Units und eine Pascal Unit ist auch eine Compilation Unit, aber mit MetaInfo.

Die Initialization Sektion wird beim Start einer Anwendung ausgeführt, als würde ein Modul auf einem Host resp. Co geladen werden. Eine Klasse plus einem Interface ist ein Abstrakter Datentyp. Du kombinierst pro Session eine andere Implementierungen miteinander. Dafür eigenen sich Interfaces sehr gut. Du lädst im Rahmen bspw. der oben genannten Planung eine andere Optimierungsstrategie (könnte man auch in eine DLL packen wie beim SAP APO).

Aus Sicht der Entwicklung. So exzessive Anwendung von Interfaces und Design Patterns macht nur Sinn, wenn der Anbieter am Punkt steht und sagt, 'I was ois, was ich brauche', denn die Anwendung basierte auf Erfahrung von 5 Leuten aus allen Teilen der Datenanalyse in der Praxis und du musst dir am Ende alles selbst schreiben. Sobald du aber spinnende (nicht spinnerte) Analysephasen und Designphasen hast, dann würde ich mal vorsichtig sein mit einer übertrieben Anwendung.

Ich kam nur als Zaungast aus der SAP Beratung vorbei und mein Sitznachbar welcher eher um 18:00 Uhr den Arbeitstag begann haben uns köstlich über die fliegenden Fetzen amüsiert.

Irgendwann mal waren die Jungs fertig und präsentierten Stolz wie Oskar das ganze. Dann habe ich mich hingesetzt und das Konzept im Delphi nachgebaut inkl. dynamischem Laden aus DLLs. Dafür konnte die Implementierung mit D7 andere Aspekte nicht ganz so. Dieses am Ende typenlose und typenbehaftet hinzubekommen bedurfte schon ein massiven Anwendung von Kniffen die nurmehr das .net mit steigender Versionszahl konnte. Mit der Zeit ist der Aspekt auch noch aus der Codegenerierung rausgewandet. Das Zeug ist an sich sehr gut gemacht und die Anwendung sauschnell.

Du hast in so einem Framework bezogen auf die Anzahl der Zeilen aus der DB und deren Inhalt andere Anforderungen. Bei 20k Zeilen aus der Planungsanwendung mit den Aufträgen, hin über die Terminierung und runter auf Produktionsschrittdetail liegen Welten. Die Datenmenge hat gewusst meine Summen bestehen aus Million von Sätzen usw... sobald du der User in Detail geht wurde dahinter der Navigationscontroller ausgetauscht usw... Da diese Anwendung vor der Berechnung der Optimierung selbst die relevaten Spalten hat ermittelt, hat man auch nicht gewusst wieviele vorverdichte Sätze kommen und Drillthrough hat teils transparent in die DB druchgegriffen und nicht mehr auf den Cache.

Nimmt man ein traditionelles Delphi grid, hätte die Zelle eben ein paar Interfaces in dem sie einerseits die Metadaten aus dem Datenbestand holt (eigene Vererbundshierarche) und auf der anderen Seite die 'Engine' welche die Summen aufdröselt.

In dem Punkt sind Interfaces enorm von Vorteil.

Java sorgt mit den Default Implementierungen für mehr Sicherheit im Sinne von Leerobjekten usw... Normalerweise behandelt man solch ein.




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