![]() |
AW: Schon wieder: Warum Interfaces
Ich finde, Interface-Programmierung verhält sich zu OOP wie die OOP zur prozeduralen Programmierung.
Grundsätzlich kannst Du jedes Problem auch ohne Interfaces und ohne OOP lösen. Und keinen interessiert es. Allerdings handelst Du dir immer irgend welche Probleme ein, in der Fachwelt wird dafür der Begriff der technischen Schulden verwendet. Keiner sieht sie, man riecht sie nicht und eines Tages schwappen sie über dich :-) Genauso wenig wie
Delphi-Quellcode:
objektorientierte Programmierung ist, ist die Definition eines Interfaces, die Implementierung und Verwendung davon automatisch auch eine sinnvolle Verwendung eines Interfaces, weil es oft schlicht und ergreifend nur mehr Arbeit ist aber keinen Vorteil bietet, weil man die Technik falsch anwendet bzw. im falschen Kontext.
type
TFoo = class(TObject) public procedure DoFoo; //mit 1.000 Zeile Code end; Interfaces sind eine logische Weiterführung der OOP, vielleicht müsste man auch sagen, eine zwingende Weiterführung. Aber es ist wie gesagt nicht damit getan ein
Delphi-Quellcode:
zu schreiben und zu Jubeln "Ich kann Interfaces", sondern dann folgen automatisch auch weiter Punkte die dann wichtig werden, denn Interfaces werfen wie vieles andere auch, mehr Fragen auf, als sie beantworten :-) Als ein Stichpunkt werfe ich hier nur mal Dependency Injection ein.
type
IFoo = Interface procedure DoFoo; end; Der große Vorteil von Interfaces eröffnet sich erst in etwas komplexeren Systemen, in denen div. Klassen miteinander kommunizieren müssen. Hier hast Du die Möglichkeit, dass diese Klassen weiterhin miteinander arbeiten könne, sich aber gegenseitig nicht mehr persönlich kennen müssen:
Delphi-Quellcode:
Und damit wir einen Konsumenten haben:
TAdresse = class()
... public function GetAnrede: String; function GetName1: String; function GetName2: String; function GetStrasse: String; .... end; TPerson = class() private FName, FVOrname: String; FStrasse: String; .... public property Adresse: TAdresse read FAdresse;
Delphi-Quellcode:
hier hast Du einen harte Kopplung wie sie in div. Projekten vorkommt und alles ist OK.
TBrief = class()
public procedure ErstelleBrief(Empfaenger: TAdresse); end; Willst Du jetzt aber TBrief durch einen Unittest jagen hast Du das Problem, dass Du zwingend auch eine Instanz von TAdresse erzeugen und übergeben musst. Kein Problem, solange TAdresse keine weiteren Abhängigkeiten hat. Sobald das System und das BOM komplexer werden, wirst Du hier immer mehr abhängige Klassen vorbereiten müssen, instanziieren müssen bis du zu dem Punkt kommst: Unittests sind scheiße, weil Du für eine Zeile Testcode, 10, 20 Zeilen Code für die Instanziierung und für die Aufräumaktionen brauchst. Und du merkst nicht, dass eigentlich dein Modell ein Problem hat. Mit Interfaces sieht das ganze so aus:
Delphi-Quellcode:
Und damit wir einen Konsumenten haben:
IAdresse = Interface
function GetAnrede: String; function GetName1: String; function GetName2: String; function GetStrasse: String; end; TPerson = class() private FName, FVOrname: String; FStrasse: String; .... public property Adresse: IAdresse read FAdresse;
Delphi-Quellcode:
Nun kann TPerson selbst entscheiden, welche Implementierung von IAdresse (es kann mehrere geben) für seinen Zwecke am besten ist. TBrief ist das aber völlig egal, durch das Interface weiß die Klasse, dass die vertraglich vereinbarten Methoden vorhanden sind und funktionieren, d.h. Du kannst TBrief dann auch damit verwenden:
TBrief = class()
public procedure ErstelleBrief(Empfaenger: IAdresse); end;
Delphi-Quellcode:
und TStudent muss kein weiteres Element deines TPerson-Frameworks kennen, muss nicht von einer gemeinsamen Basisklasse abgeleitet werden,....
type
TStudent = class(X,IAdresse) ... public function GetAnrede: String; function GetName1: String; function GetName2: String; function GetStrasse: String; Kommen wir aber nochmal zurück zur TPerson: ich habe oben geschrieben, dass TPerson entscheidet welche Implementierung von IAdresse es verwendet. Das ist aber schon wieder ein Fehler! Weil du baust dir hier wieder über die Hintertür eine harte Kopplung von 2 Klassen ein. Und genau das kann später wieder zu einem Problem werden, weil du dann an dem Punkt bis: Verflixt, jetzt wollte ich harte Kopplung vermeinden, muss aber halt irgend wo implementieren:
Delphi-Quellcode:
Lösung für dieses Problem bietet die Dependency Injection: Ich kopple das Interface nicht an einer bestimmten Stelle mit einer konkreten Implementierung sondern biete über eine Liste (sog. Container) verschiedene Möglichkeiten an (oder vielleicht auch nur eine) wie IAdresse implementiert sein soll. TPerson instanziiert dann die IAdresse nicht selbst, sondern fragt bei der Liste an: Gib mir eine Instanz von IAdresse. Aber den Part könnte Stefan G. nun wirklich besser erklären :-)
TPerson = class()
private FName, FVOrname: String; FStrasse: String; .... public property Adresse: IAdresse read FAdresse; ... TPerson.Create() begin FAdresse := TFooAdresse.Create(); end; wie auch bei der OOP ist auch bei Interfaces das Problem: Mit 2 Sätzen und einem einfachen Beispiel ist das nicht erklärt.... |
AW: Schon wieder: Warum Interfaces
All das ist ja alles sehr interessant und auch absolut sinnvoll. Nebenbei, ich gehöre auch zu denen, die gern was mit Interfaces machen würden (und auch schon mögliche Projekte in der Pipeline haben), aber irgendwie über eine Brücke nicht rüberkommen.
Meine Blockade ist die vielbeschworene Sprachenunabhängigkeit. Man schreibt eine DLL in Delphi, schraubt ein paar Interfaces um die Klassen herum und kann das dann in C# verwenden. Wie soll das aussehen, kann da jemand einen 30-Zeiler (okay, weils 2 Sprachen sind, 60-Zeiler) zusammenschustern, wo man das mal sehen kann ? |
AW: Schon wieder: Warum Interfaces
@DeddyH + vor allem Lemmy
:thumb: So etwa hätte ich das auch sagen wollen... :oops: @OlafSt Dazu hat Mavarik ja etwas angekündigt. Ich selbst habe diesbezüglich keine Erfahrungen. |
AW: Schon wieder: Warum Interfaces
Zitat:
Zitat:
Zitat:
Wenn ich etwas in einer Liste speichere, und das "Ding" an andererstelle Frei geben, dann wollte ich es - wenn es in der Liste ist - sicherlich noch verwenden, oder? Ich brauche einfach nicht die Try Finally ketten...
Delphi-Quellcode:
oder
begin
AFoo := TFoo.Create; try ABar := TBar.Create; try AFile := TFilestream.Create(..) try ABar.LoadFormStream(AFile); AFoo.Add(ABar); AFoo.Machwas; finally AFile.Free; end; finally ABar.free; // ggf. könnte ich das AFoo überlassen, aber dann würde ich im Source immer darüber stolpern. end; finally AFoo.Free; end; end;
Delphi-Quellcode:
Mavarik :coder:
begin
IFoo := TFoo.Construct(TBar.Construct(TiFile.Construct(..))); IFoo.Machwas; end; |
AW: Schon wieder: Warum Interfaces
Zitat:
Ich bin der Meinung... solange man der alleinige Entwickler an einem Projekt ist, ist es vollkommen egal was man verwendet. Hauptsache man versteht seinen eigenen Code, er funktioniert und lässt sich gut ändern. Gegenfrage: haben Interfaces irgendeinen Vorteil in der erzeugten Programmdatei? |
AW: Schon wieder: Warum Interfaces
Zitat:
Aber 90% der Vorteile sind für Dich als Entwickler... Ein Beispiel:
Code:
ist in der exe 1. Zeile
SchreibeDatenWeg.exe /SQLite
SchreibeDatenWeg.exe /REST:https://www.meinserver.de SchreibeDatenWeg.exe /MySQL
Delphi-Quellcode:
IDatenZiel := TDatenWriter.CreateFromParamOrDefault(Params);
|
AW: Schon wieder: Warum Interfaces
Also kann man grundlegend festhalten, dass Interfaces immens zur Code-Minimierung beitragen wenn nicht sogar dafür vorgesehen sind?
|
AW: Schon wieder: Warum Interfaces
Zitat:
Höchstwahrscheinlich - ab Januar, wenn ich meine Youtube-Tutorial Reihe zu meinem FDK beginne. Das FDK arbeiten hauptsächlich - aus den genannten Gründen (Windows & ARC) - mit Interfaces. |
AW: Schon wieder: Warum Interfaces
Ich sehe aktuell wie bereits geschrieben den Vorteil in Interfaces, wenn man DLLs programmieren will um seinen Code irgendwie auszulagern. Dann haben die Dinger extreme Vorteile.
Ein anderer Punkt an dem man Interfaces verwenden kann ist eben dann, wenn man 2 unterschiedliche Klassen hat, die aber die gleichen Informationen zur Anzeige auf der GUI zur Verfügung stellen sollen. Jede Klasse verarbeitet die Informationen allerdings anders und daher ist auch das angezeigte Ergebnis ein anderes. Die Abfrage im Programm bleibt aber immer gleich.
Delphi-Quellcode:
ITest = interface
function GetValue: string; end;
Delphi-Quellcode:
TMyClass1 = class(TInterfacedObject, ITest)
function GetValue: string; end; implementation function TMyClass1.GetValue: string; begin Result := 'Das ist der Wert aus MyClass1'; end;
Delphi-Quellcode:
TMyClass2 = class(TInterfacedObject, ITest)
function GetValue: string; end; implementation function TMyClass2.GetValue: string; begin Result := 'Das ist der Wert aus MyClass2'; end;
Delphi-Quellcode:
Hier habe ich jetzt den Vorteil, dass ich einfach eine beliebige Interface Instanz übergeben bekomme aus der ich mir dann mit dem gleichen Aufruf die benötigten Informationen ziehen kann. Das würde bei Klassen zwar auch gehen, aber dafür müssten alle übergebenen Klassen von der gleichen Basisklasse abgeleitet werden. Diese müsste wiederum eine abstrakte Methode GetValue zur Verfügung stellen, welche von jeder Ableitung implementiert werden muss. In diesem Fall sind Interfaces deutlich leichter zu handlen.
TForm1 = class(TForm)
procedure ShowValue(FromInterface: ITest); end; implementation function TForm1.ShowValue(FromInterface: ITest); begin Label1.Caption := FromInterface.GetValue; end; Wenn man allerdings nur eine Klasse hat welche Informationen zur Verfügung stellen soll, dann lohnt sich ein Interface meines Erachtens nicht wirklich. Man hat nur den Vorteil den Mavarik angesprochen hat. Diese try..finally Blöcke und das Object.Free fallen weg. Wenn man die Interface Instanzen dann korrekt verwendet, dann sollte man auch kein Problem mit der Referenzzählung haben. |
AW: Schon wieder: Warum Interfaces
Was mir noch einfällt.
Beispiel mit : IStreamPersist
Delphi-Quellcode:
Jetzt kann ich abfragen, ob ein Object, dass Interface unterstützt. Und dann in einen Stream schreiben oder vom Stream lesen...
TFoo = Class(TInterfacedObject,ICanCompress);
... TBar = Class(TInterfacedObject,ICanCompress,IStreamPersist) |
Alle Zeitangaben in WEZ +1. Es ist jetzt 11:06 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