|
![]() |
|
Registriert seit: 24. Aug 2004 140 Beiträge Delphi XE8 Professional |
#1
Hallo,
Der einzige Grund, wieso ich bisher bewusst ein Interface eingesetzt habe (von COM und sowas mal abgesehen), war, dass ich Mehrfachvererbung für eine Klasse brauchte.
Ist mir bis heute unverständlich, wieso Delphi sowas nicht unterstützt.
Ich finde, Interface-Programmierung verhält sich zu OOP wie die OOP zur prozeduralen Programmierung.
![]() OOP ist nur syntaktischer Zucker.
Eine Klasse besteht aus einer Schnittstelle und deren Implementierung. Eine Klasse kann die Schnittstelle zusammen mit deren Implementierung einer anderen Klasse erben. Und das Vererben der Implementierung ist OOP. Ein Interface ist somit quasi eine halbe Klasse, nämlich die Schnittstelle. Der Vorteil von OOP ist die Codereduzierung. Ohne OOP müsste man bei einem TNumberEdit größtenteils das komplette TEdit bis runter zu TObject nochmal neu implementieren. Mit OOP ist das nur eine klitzekleine Vererbung mit dem überschreiben einer virtuellen Methode. Interfaces bringen keine weitere Codereduzierung. Falsch eingesetzt erhöhen sie sogar die Menge an Code. Und hiermit stelle ich an die Interface-Prediger mal eine Aufgabe. Baut doch mal TNumberEdit, TEdit bis runter zu TObject Interface-like nach ohne Klassenvererbung und ohne die Klassen aus VCL/RTL einzusetzen. Aber bitte mit Implementierung. Mich würde mal interessieren wie das denn aussehen würde. Interface-Programmierung kann sich nicht zu OOP verhalten, weil sie teil dieser ist. Grundsätzlich kannst Du jedes Problem auch ohne Interfaces und ohne OOP lösen.
Und keinen interessiert es.
Genauso wenig wie
Delphi-Quellcode:
objektorientierte Programmierung ist,
type
TFoo = class(TObject) public procedure DoFoo; //mit 1.000 Zeile Code end;
Delphi-Quellcode:
ist OOP, wegen dem TEncoding.UTF8.GetBytes
.
programm Test;
uses System.SysUtils; var X: TBytes; begin X:= TEncoding.UTF8.GetBytes('Test'); end; 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.
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
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....
Ich verwende auch Interfaces, aber sparsam. Bei mir ist das Verhältnis zwischen Interfaces und reiner Klassenvererbung gefühlt gleich dem in der VCL/RTL. Eigentlich benutze ich Interfaces nur da wo ich bei gemeinsamer Schnittstelle keine gemeinsame Vererbungslinie aufbauen kann. Für dieses Problem wünsche ich mir aber eine andere Lösung. Und jetzt mal ein Beispiel wie die andere Lösung aussehen müsste und wo Interfaces absolut nicht helfen.
Delphi-Quellcode:
einbeliebigername.
Type
TCustomLabeledControl<T: TControl>= class(T) strict private fLabel: TLabel; strict protected function GetLabelControlClass: TLabelClass; virtual; protected // Und viele override public constructor Create(AOwner: TComponent); override; procedure SetBounds(ALeft: Integer; ATop: Integer; AWidth: Integer; AHeight: Integer); override; // Und noch mehr override end; TLabeledEdit= class(TCustomLabeledControl<TEdit>); TLabeledComboBox= class(TCustomLabeledControl<TComboBox>); TLabeledMemo= class(TCustomLabeledControl<TMemo>); TLabeledRichEdit= class(TCustomLabeledControl<TRichEdit>); implementation constructor TCustomLabeledControl<T: TControl>.Create(AOwner: TComponent); begin inherited; fLabel:= GetLabelControlClass.Create(nil); // Oder auch Self wie man mag end; function TCustomLabeledControl<T: TControl>.GetLabelControlClass: TLabelClass; begin Result:= TLabeledControlManager.Instance.GetLabelControlClass(…); // Einen String, Enum, Self oder was auch immer passent ist übergeben end; procedure TCustomLabeledControl<T: TControl>.SetBounds(ALeft: Integer; ATop: Integer; AWidth: Integer; AHeight: Integer); begin inherited; fLabel.SetBounds(…); end; |
![]() |
einbeliebigername |
Öffentliches Profil ansehen |
Mehr Beiträge von einbeliebigername finden |
Jim Carrey
(Gast)
n/a Beiträge |
#2
Wenn ich das bisher richtig verstehe geht es bei Interfaces quasi vordergrundig um die Komprimierung des Codes.
|
![]() |
Registriert seit: 8. Feb 2012 Ort: OWL 32 Beiträge Delphi 10.1 Berlin Starter |
#3
Wenn ich das bisher richtig verstehe geht es bei Interfaces quasi vordergrundig um die Komprimierung des Codes.
Interfaces und somit OOP stehen für ein Paradigma, dass Wartbarkeit und Erweiterbarkeit, sowie Architektur von Software im Fokus hat. Siehe z.b.: ![]() Das ist kein Thema, dass man mal eben in 30 Minuten vollständig erfassen kann. Mir kommt es in diesem Thread eh so vor, als ob man beim Begriff Interface(im Sinne von OOP), nicht von der selben Definition ausgeht. Der eine spricht von DLL's oder einer anderen Schnittstellentechnik, der andere missbraucht Interfaces für die sogenannte Mehrfachvererbung. Siehe dazu vielleicht: ![]() Vielleicht liegt das an der Geschichte von Pascal/Object-Pascal(als ehemals imperative Programmiersprache) und die stete Bindung an die DLL-Hell. Java sowie C# machen ja eigentlich sehr schnell verständlich, was und wofür Interfaces eingesetzt werden. C++, ich glaube auch Perl und Phyton, sind momentan die einzigen populären Sprachen, die auf Mehrfachvererbung setzen. Es gibt Gründe dafür und auch gute Gründen dagegen. Das muss dann jeder für sich ausmachen. Ich bin da persönlich sehr zufrieden, dass Mehrfachvererbung in Java, C# und Delphi nicht möglich ist. Die Architektur einer Software ist schon kompliziert genug. |
![]() |
Registriert seit: 8. Jun 2002 Ort: Berglen 2.395 Beiträge Delphi 10.4 Sydney |
#4
Der Vorteil von OOP ist die Codereduzierung. Ohne OOP müsste man bei einem TNumberEdit größtenteils das komplette TEdit bis runter zu TObject nochmal neu implementieren. Mit OOP ist das nur eine klitzekleine Vererbung mit dem überschreiben einer virtuellen Methode. Interfaces bringen keine weitere Codereduzierung. Falsch eingesetzt erhöhen sie sogar die Menge an Code.
... Und hiermit stelle ich an die Interface-Prediger mal eine Aufgabe. Baut doch mal TNumberEdit, TEdit bis runter zu TObject Interface-like nach ohne Klassenvererbung und ohne die Klassen aus VCL/RTL einzusetzen. Aber bitte mit Implementierung. Mich würde mal interessieren wie das denn aussehen würde. Genauso wenig wie
Delphi-Quellcode:
objektorientierte Programmierung ist,
type
TFoo = class(TObject) public procedure DoFoo; //mit 1.000 Zeile Code end; Selbst ein schlichtes
Delphi-Quellcode:
ist OOP, wegen dem TEncoding.UTF8.GetBytes
.
programm Test;
uses System.SysUtils; var X: TBytes; begin X:= TEncoding.UTF8.GetBytes('Test'); end; Interfaces sind eine logische Weiterführung der OOP, vielleicht müsste man auch sagen, eine zwingende Weiterführung.
Das bekommst du aber auch ohne Interface hin. Ich sage nur reine abstrakte Klassen. Die haben mit Interfaces was gemeinsam. Denen fehlt auch die Implementierung der Schnittstelle.
sicher bekomme ich das auch mit einer abstrakten Basisklasse hin. Was ist aber, wenn ich keine gemeinsame abstrakte Basisklasse definieren will / kann? Wenn ich keine Interfaces hätte müsste ich zwangsläufig irgend wo aus dem Framework eine gemeinsame Elternklasse raus suchen und von der einen Erben erstellen - selbst dann wenn ich das nicht will, weil ich damit Abhängigkeiten eingehe. Mit Interfaces muss ich das nicht. Habe ich dagegen ein Interface zur Verfügung muss ich lediglich das implementieren und ich kann eine externe Funktionalität nutzen. Schau dir nochmal mein Beispiel an: TStudent muss von "Framework" um TBrief und TPErson nichts kennen, es musss lediglich IAdresse implementieren und schon kann ich die beiden Frameworks gemeinsam nutzen. |
![]() |
Registriert seit: 5. Aug 2013 265 Beiträge |
#5
wäre das ein C++ Forum würde ich dir recht geben und hätte das auch nicht geschrieben. Dort bestand von Anfang an die Trennung zwischen Interface (Header) und Implementierung, das gab es in Delphi erst als es auch Interfaces gab. OOP geht in Delphi wunderbar ohne Interfaces weil alles in einer Datei implementiert wird (werden muss) die Möglichkeit das zu trennen bieten erst Interfaces. Und die waren in C++ von Anfang an so eingeplant. Daher bleibe ich dabei: Die sind die logische Fortsetzung.
Die vom COM bekannten Interfaces kann man sogar mit normalem C implementieren und nutzen. |
![]() |
Registriert seit: 8. Jun 2002 Ort: Berglen 2.395 Beiträge Delphi 10.4 Sydney |
#6
wäre das ein C++ Forum würde ich dir recht geben und hätte das auch nicht geschrieben. Dort bestand von Anfang an die Trennung zwischen Interface (Header) und Implementierung, das gab es in Delphi erst als es auch Interfaces gab. OOP geht in Delphi wunderbar ohne Interfaces weil alles in einer Datei implementiert wird (werden muss) die Möglichkeit das zu trennen bieten erst Interfaces. Und die waren in C++ von Anfang an so eingeplant. Daher bleibe ich dabei: Die sind die logische Fortsetzung.
Die vom COM bekannten Interfaces kann man sogar mit normalem C implementieren und nutzen. |
![]() |
Registriert seit: 5. Aug 2013 265 Beiträge |
#7
Headerdateien sind keine Interfaces. Nur, weil in Delphi der Anfang der Quelldatei sich "Interface" nennt, ist der dazu in C++ analoge Teil (die Headerdatei) noch lange keins.
Headerdateien sind dazu da, Vorwärtsdeklarationen und Extern-Deklarationen für die Verwendung der in der C++-Datei enthaltenen Implementierung bereitzustellen. Interfaces (und bei C++ abstrakte Basisklassen) werden verwendet, - um verschiedene Implementierungen von gleichartigem Code verwenden zu können - um Fassaden- und Adapter-Patterns zu realisieren - um Unit-Tests viel (!) einfacher zu machen, selbst wenn die getesteten Klassen im System "mittendrin" verwendet wird. In Delphi wird unglücklicherweise die Interface-Verwendung mit der Referenzzählung und automatischer Freigabe gekoppelt. Deshalb wird das auch häufig missverstanden. |
![]() |
Registriert seit: 19. Okt 2003 Ort: Heilbronn 689 Beiträge Delphi 10.4 Sydney |
#8
Ich weiss nicht, ob es genannt wurde, woltle jetzt keine 6 Seiten lesen :
Mit einem interface kann ich 2 komplett verschiedene Klassen miteinander verbinden, zum Bsp. TTreeView und TListView, da könnte man dann ein AddItem in einem interface hinzufügen und dann ohne die klasse zu kennen über das interface AddItem aufrufen. |
![]() |
Registriert seit: 17. Sep 2006 Ort: Barchfeld 27.656 Beiträge Delphi 12 Athens |
#9
Nach meinem Verständnis ist genau das der Sinn von Interfaces: Abstraktion. Angenommen, wir haben eine Klassenstruktur mit 2 Zweigen: TFoo und TBar. Wollen wir jetzt einer Methode eine Instanz als Parameter übergeben, welche wiederum eine Methode der übergebenen Instanz aufruft, und diese Instanz soll entweder TFoo oder TBar (oder eine Ableitung einer der beiden) sein, dann haben wir 3 Möglichkeiten:
1. Deklaration einer Basisklasse mit dieser Methode, von der sowohl TFoo als auch TBar abgeleitet werden, der Parameter ist dann vom Typ der Basisklasse 2. Eine Abfrage innerhalb der aufrufenden Methode, ob die Parameter-Instanz TFoo oder TBar ist, der Parameter ist dann vom Typ TObject 3. Definition eines Interfaces, das von irgendeiner (oder auch mehreren) Klasse innerhalb der Struktur implementiert wird und Deklaration des Parameters als Typ dieses Interfaces 2 ist die unsauberste und unflexibelste Lösung, da sind wir uns wohl einig. 1 ist ein gangbarer Weg, kann aber bei mehreren solcher Fälle schnell unübersichtlich werden und dazu führen, dass schon die Basisklasse mit Methoden überfrachtet wird, die erst weiter unten im Zweig tatsächlich benötigt werden. Und zu 3: wie war nochmal der Titel dieses Threads? ![]()
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 |
![]() |
Registriert seit: 24. Aug 2004 140 Beiträge Delphi XE8 Professional |
#10
Hallo,
Wenn ich das bisher richtig verstehe geht es bei Interfaces quasi vordergrundig um die Komprimierung des Codes.
Der Vorteil von OOP ist die Codereduzierung. Ohne OOP müsste man bei einem TNumberEdit größtenteils das komplette TEdit bis runter zu TObject nochmal neu implementieren. Mit OOP ist das nur eine klitzekleine Vererbung mit dem überschreiben einer virtuellen Methode. Interfaces bringen keine weitere Codereduzierung. Falsch eingesetzt erhöhen sie sogar die Menge an Code.
... Und hiermit stelle ich an die Interface-Prediger mal eine Aufgabe. Baut doch mal TNumberEdit, TEdit bis runter zu TObject Interface-like nach ohne Klassenvererbung und ohne die Klassen aus VCL/RTL einzusetzen. Aber bitte mit Implementierung. Mich würde mal interessieren wie das denn aussehen würde. Zusammenfassung von Daten und Methoden in Verbindung mit Vererbung ist OOP. Vererbung ist das was dabei die Wiederverwendbarkeit von Sourcecode verbessert. Die Zusammenfassung von Daten und Methoden verbessert die Verständlichkeit. Damit man so viel wie möglich Code wiederverwendet setzt man Vererbung ein wo es nur geht. Interfaces bleiben dann nur für die Spezialfälle, wo Vererbung nicht mehr funktioniert. Bei einer Schnittstelle zwischen DLL und Anwendung kommt man mit Vererbung nicht zum Ziel, da braucht man und sollte man Interfaces einsetzen. Aber bei so simplen wie TAdresse gleich mit Interface anzufangen bläht den Code nur unnötig auf. Genauso wenig wie
Delphi-Quellcode:
objektorientierte Programmierung ist,
type
TFoo = class(TObject) public procedure DoFoo; //mit 1.000 Zeile Code end; Selbst ein schlichtes
Delphi-Quellcode:
ist OOP, wegen dem TEncoding.UTF8.GetBytes
.
programm Test;
uses System.SysUtils; var X: TBytes; begin X:= TEncoding.UTF8.GetBytes('Test'); end; Interfaces sind eine logische Weiterführung der OOP, vielleicht müsste man auch sagen, eine zwingende Weiterführung.
Das bekommst du aber auch ohne Interface hin. Ich sage nur reine abstrakte Klassen. Die haben mit Interfaces was gemeinsam. Denen fehlt auch die Implementierung der Schnittstelle.
sicher bekomme ich das auch mit einer abstrakten Basisklasse hin. Was ist aber, wenn ich keine gemeinsame abstrakte Basisklasse definieren will / kann? Habe ich dagegen ein Interface zur Verfügung muss ich lediglich das implementieren und ich kann eine externe Funktionalität nutzen. Schau dir nochmal mein Beispiel an: TStudent muss von "Framework" um TBrief und TPErson nichts kennen, es musss lediglich IAdresse implementieren und schon kann ich die beiden Frameworks gemeinsam nutzen.
Und schaue dir doch mal bitte mein Gegenbeispiel mit den LabeledControls an und sag mir wie die Zauberinterfaces (Auch diesen Begriff habe ich aus vielen Aussagen überspitzt zusammengesetzt) das Problem lösen. Interfaces können nicht zauber. Sie sind auch nicht die einzige Möglichkeit Abhängigkeiten aufzulösen. Sie sind sogar in gewisser Weise gefährlich. Ich weiss nicht, ob es genannt wurde, woltle jetzt keine 6 Seiten lesen :
Mit einem interface kann ich 2 komplett verschiedene Klassen miteinander verbinden, zum Bsp. TTreeView und TListView, da könnte man dann ein AddItem in einem interface hinzufügen und dann ohne die klasse zu kennen über das interface AddItem aufrufen. 2 ist die unsauberste und unflexibelste Lösung, da sind wir uns wohl einig. 1 ist ein gangbarer Weg, kann aber bei mehreren solcher Fälle schnell unübersichtlich werden und dazu führen, dass schon die Basisklasse mit Methoden überfrachtet wird, die erst weiter unten im Zweig tatsächlich benötigt werden. Und zu 3: wie war nochmal der Titel dieses Threads?
![]() Was ich noch für Interfases gelten lassen würde, währ das Argument, dass man durch Interfases die Schnittstelle für eine Funktionalität deutlicher abgrenzen kann. Das müsste man aber ohne Zusätzliche Implementierung schaffen können. Also ein einfaches sinnfreies Beispiel. Man hat mehrere Formularklassen. Bei jeder will man die Caption von außen verändern können. Der Teil der Schnittstelle in der Basisklasse von den Formularklassen ist das Property Caption. Den Rest braucht man nicht für diese Aufgabe. Also könnte man jetzt mittels eines Interface den Teil abgrenzen, wie folgt:
Delphi-Quellcode:
Die Implementierung des Interface führt nicht zu mehr Code, da das was im Interface verlangt wird bereit in der Basisklasse vorhanden ist. Leider macht Delphi da einem wieder mitunter einen dicken Strich durch die Rechnung. Bei Properties in Interfaces muss man Getter (wenn man lesen will) und Setter (wenn man schreiben will) angeben. Das führt im konkreten Beispiel dazu, dass man in jeder Formularklasse, welche iFormCaption implementiert auch den Getter und Setter implementieren muss. Die sehen aber immer identisch aus. Selbst wenn man es schafft Getter und Setter in einer Basisklasse zu implementieren, ist das noch ein unnötiges aufblähen des Codes.
iFormCaption= interface
property Caption: string; end; TMyForm= class(TForm, iFormCaption) end; einbeliebigername. |
![]() |
einbeliebigername |
Öffentliches Profil ansehen |
Mehr Beiträge von einbeliebigername finden |
Ansicht |
![]() |
![]() |
![]() |
ForumregelnEs 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
|
|
Nützliche Links |
Heutige Beiträge |
Sitemap |
Suchen |
Code-Library |
Wer ist online |
Alle Foren als gelesen markieren |
Gehe zu... |
LinkBack |
![]() |
![]() |