AGB  ·  Datenschutz  ·  Impressum  







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

Interfaces + Factorys

Ein Tutorial von stahli · begonnen am 29. Jan 2015 · letzter Beitrag vom 22. Feb 2015
Antwort Antwort
Seite 4 von 7   « Erste     234 56     Letzte »    
Benutzerbild von stahli
stahli
Registriert seit: 26. Nov 2003
Ich habe länger gebraucht, um das Konzept und die Benutzung von Interfaces in Delphi zu verstehen.

Es gibt zwar einige Erklärungen und Beispiele, die waren jedoch nicht immer für Neulinge in dem Themenbereich geeignet.

Daher habe ich hier einmal versucht, meine Erkenntnisse und bisherigen Erfahrungen zusammenzufassen.
Die Videos sind nicht hochprofessionell, aber vielleicht helfen sie einigen Interessierten, die Interfaces in Delphi schneller zu verstehen und anwenden zu können...


Die Erläuterungen habe ich in 3 Blöcke aufgeteilt:

Einige Basics zu Interfaces in Delphi.
http://youtu.be/MEX1836SvgE

Etwas detaillierte Infos und Anwendungsbeispiele.
http://youtu.be/IyvqZAUSJGY

Verbergen der Klassen und Arbeiten nur mit Interfaces.
http://youtu.be/NcbIV0SUFJs


Wenn ich etwas fehlerhaft oder unvollständig erläutert habe, dann können wir das hier gern richtig stellen...
Angehängte Dateien
Dateityp: zip Interfaces_Factory.zip (89,1 KB, 72x aufgerufen)
Stahli
http://www.StahliSoft.de
---
"Jetzt muss ich seh´n, dass ich kein Denkfehler mach...!?" Dittsche (2004)
 
Benutzerbild von stahli
stahli

 
Delphi 11 Alexandria
 
#31
  Alt 1. Feb 2015, 12:57
Ok, super!

Das IMotorBetrieben ist eine bessere Bezeichnung als IHasMotor. Aber konzeptionell meinte ich das Gleiche.

Allerdings reicht Supports(aContext, IMotorBetrieben) für einen Zugriff auf den Motor noch nicht ganz aus, weil der ja nil sein könnte.
Also muss dazu ggf. nochmal eine Prüfung her.

Wenn man ganz mutig denkt, könnte ein späterer Fluxkompensator natürlich zu unserer Motor-Schnittstelle nicht mehr kompatibel sein.
Aber ok, das muss man wohl akzeptieren und in 20 Jahren die Schnittstellen ggf. nochmal überarbeiten.


Die Enten und Fahrzeuge finde ich aber im Zusammenhang mit Schnittstellenbeispielen dennoch nicht schlecht, da man sieht, dass völlig unterschiedliche Klassen (ohne direkte gemeinsame Basisklasse) die gleichen Schnittstellen bedienen können.
  Mit Zitat antworten Zitat
Benutzerbild von Stevie
Stevie

 
Delphi 10.1 Berlin Enterprise
 
#32
  Alt 1. Feb 2015, 13:00
Bei einigen Aktionen solltest du überlegen, ob sie wirklich zu der Klasse gehören.

Zum Beispiel die Methode Tanken gehört sicherlich nicht in die Klasse Motor oder Auto oder Boot.
Diese Klassen/Interfaces sind nicht die Akteure, sondern die Argumente für die Aktion betanken, welche ein IBetankbar Interface oder sowas haben.
Stefan
  Mit Zitat antworten Zitat
Benutzerbild von Sir Rufo
Sir Rufo

 
Delphi 10 Seattle Enterprise
 
#33
  Alt 1. Feb 2015, 13:08
Wenn nil zulässig ist, dann hat man das Bei Google suchenNULL-Pattern oder man wirft idealerweise ASAP eine Exception (Guard).

Was richtig ist hängt vom Gesamtkontext ab und kann pauschal nicht beantwortet werden. Ich würde das aber zunächst völlig herauslassen und eben immer bei IMotorBetrieben einen Motor voraussetzen.
  Mit Zitat antworten Zitat
Benutzerbild von Sir Rufo
Sir Rufo

 
Delphi 10 Seattle Enterprise
 
#34
  Alt 1. Feb 2015, 14:05
Um Interfaces näher zu bringen würde ich eben ganz andere Beispiele nehmen, etwas, das man anfassen kann.

Wir haben z.B. eine Anwendung, die Dateien in einen Stream liest, die dann von der Anwendung weiterverarbeitet werden.

Dazu hat man sich eine schöne Klasse gebaut, die nun eine Datei in einen Stream einliest und diesen zurückgibt.
Delphi-Quellcode:
TFileStreamHandler = class
procedure GetStream( AFilename : string; AStream : TStream );
procedure WriteStream( AFilename : string; AStream : TStream );
end;
Vor allem ist das schick, wenn wir die Datei "$(Archiv1)\12345.doc" lesen wollen, setzt diese Klasse das gleich in den korrekten Pfad um Sehr geschmeidig.

Delphi-Quellcode:
procedure Foo( AHandler : TFileStreamHandler );
var
  LStream : TStream;
begin
  AHandler.GetStream( '$(Import)\Bild.jpg', LStream );
  AHandler.WriteStream( '$(Archiv)\123456.jpg', LStream );
end;
Alles ist gut, bis zu dem Tag, wo man feststellt, dass es besser wäre, diese Streams nicht wirklich in Dateien, sondern in einer Datenbank zu halten. Kein Problem, ändere ich einfach die Klasse ... hmmm, wie machen wir das bloss. Diese Klasse benutzen wir an jeder Stelle im Programm für jede Datei, auch für die, die weiter als Datei dort liegen.

Ganz einfach mit Interfaces:
Delphi-Quellcode:
IStreamHandler = interface
  procedure GetStream( AFilename : string; AStream : TStream );
  procedure WriteStream( AFilename : string; AStream : TStream );
end;

// die alte Klasse tut es ja noch
TFileStreamHandler = class( TInterfacedObject, IStreamHandler )
procedure GetStream( AFilename : string; AStream : TStream );
procedure WriteStream( AFilename : string; AStream : TStream );
end;
Die Anwendung ändere ich um, damit die mit dem Interface arbeitet
Delphi-Quellcode:
procedure Foo( AHandler : IStreamHandler );
var
  LStream : TStream;
begin
  AHandler.GetStream( '$(Import)\Bild.jpg', LStream );
  AHandler.WriteStream( '$(Archiv)\123456.jpg', LStream );
end;
Jetzt benötigen wir eine neue Klasse, die uns den Dateiinhalt aus der Datenbank holt/schreibt
Delphi-Quellcode:
TDatabaseStreamHandler = class( TInterfacedObject, IStreamHandler )
procedure GetStream( AFilename : string; AStream : TStream );
procedure WriteStream( AFilename : string; AStream : TStream );
end;
Und jetzt noch eine, die den Zugriff routet
Delphi-Quellcode:
TRoutedStreamHandler = class( TInterfacedObject, IStreamHandler )
procedure GetStream( AFilename : string; AStream : TStream );
procedure WriteStream( AFilename : string; AStream : TStream );
end;
Und erzeuge die Klasse z.B. mit
Delphi-Quellcode:
function GetStreamHandler : IStreamHandler;
var
  LFiles : IStreamHandler;
  LDatabase : IStreamHandler;
begin
  LFiles := TFileStreamHandler.Create;
  LDatabase := TDatabaseStreamHandler.Create( 'Server1', 'user', 'pass', 'archivdb' );

  Result := TRoutedStreamHandler.Create(
  {Default} LFiles,
  {} [
    {} 'Archiv', LDatabase,
    {} 'Archiv1', LDatabase ] );
end;
Welchen noch so ominösen Stream-Speicherort ich mir auch noch ausdenken werde (oder mir ausgedacht wird) ich erzeuge eine konkrete Klasse für diesen konkreten Ort und klatsche das in den Router mit rein. Die Anwendung arbeitet ohne weitere Änderungen wie gehabt weiter.
  Mit Zitat antworten Zitat
OlafSt

 
Delphi 10.2 Tokyo Professional
 
#35
  Alt 1. Feb 2015, 19:55
Das Beispiel mit dem StreamHandler ist derart unverständlich, das ich empfehle, den Post zu entfernen. Das läßt Leute, die so unsicher auf dem Gebiet sind wie ich, gleich wieder an sich zweifeln.
  Mit Zitat antworten Zitat
Benutzerbild von stahli
stahli

 
Delphi 11 Alexandria
 
#36
  Alt 1. Feb 2015, 20:06
Den letzten Abschnitt verstehe ich auch nicht.
  Mit Zitat antworten Zitat
Dejan Vu
 
#37
  Alt 1. Feb 2015, 20:12
Der StreamHandler definiert (so wie ich das verstanden habe) nur den Vertrag, das Daten mit einem Namen versehen in einen Stream geschrieben und aus ihm gelesen werden.

Wieso weiß der IStreamHandler eigentlich, das es Filenamen gibt? Sollte ihm das nicht komplett egal sein, ob es ein File-, Registry-, Tabellen-, oder Kleinkolonieseiteneingangs-namen handelt?

Du hast geschrieben, das ihr das erst später bemerkt habt. Klar, hinterher ist man immer schlauer. Daher als Tipp (für alle Leser): Beim Design von Klassen und Interfaces sollten nur gerade so viele Informationen in das Design gesteckt werden, wie die Klasse/das Interface benötigt.

Wenn also ein 'IStreamHandler' deklariert wird, reich es, einen 'Name' (oder treffender, falls ich das richtig verstanden habe: 'Identifier') anzugeben. Einfach deshalb, weil im Namen des Interfaces nirgends etwas mit 'File' steht.

Ansonsten halte ich das Beispiel für sehr anschaulich. Es ist auch ein tolles Beispiel, wie sehr man später davon profitiert, früher mal etwas richtig gemacht zu haben (der Name lässt sich ja leicht mit Refactoring noch zurechtrücken).

PS: Was der TRoutedStreamHandler macht, erschließt sich mir auch nicht.

Aber man kann z.B. einen Streamhandler für TCP schreiben, oder für eine INI-Datei, oder oder oder.
  Mit Zitat antworten Zitat
Benutzerbild von stahli
stahli

 
Delphi 11 Alexandria
 
#38
  Alt 1. Feb 2015, 23:50
Bin gerade noch auf eine kleine Eigenart gestoßen...

Folgendes kompiliert und funktioniert problemlos:
Delphi-Quellcode:
  IFlieg = interface
    ['{0E839812-DAB3-47F0-AF3E-AB05FFDD6CE6}']
    procedure Flieg;
    function get_X: Integer;
    procedure set_X(Value: Integer);
    property X: Integer read get_X write set_X;
  end;

  TVogel = class(TInterfacedObject, IFlieg)
  private
    fX: Integer;
  public
    procedure Flieg;
    function get_X: Integer;
    procedure set_X(Value: Integer);
    //property X: Integer read get_X write set_X;
  end;
Man kann also die Property in der Klasse weglassen (Getter bzw. Setter reichen) und kann die Property dennoch benutzen (auch die Codevervollständigung bietet sie an).

Ich denke mal, der besseren Lesbarkeit halber sollte man sie dennoch in der Klasse deklarieren.
  Mit Zitat antworten Zitat
Jumpy

 
Delphi 6 Enterprise
 
#39
  Alt 2. Feb 2015, 10:51
PS: Was der TRoutedStreamHandler macht, erschließt sich mir auch nicht.
Ich habe das so verstanden, dass TRoutedStreamHandler "entscheidet" welcher der tatsächlichen anderen Handler-Klassen (die ihm im OnCreate eingeflöst werden) die Anfrage nun bearbeitet. Wäre aber nett zu sehen, wie es bei ihm im Inneren aussieht, denn auch wenn ich glaube verstanden zu haben was er macht, weiß ich nicht wie.
Ralph
  Mit Zitat antworten Zitat
Benutzerbild von Stevie
Stevie

 
Delphi 10.1 Berlin Enterprise
 
#40
  Alt 2. Feb 2015, 11:00
wie es bei ihm im Inneren aussieht
Genau das ist der Punkt, wenn man mit Interfaces arbeitet.
Sofern man sich an die Regeln hält, dass die Implementierungen der Interfaces sich gemäß der Spezifikationen verhalten, ist es unerheblich, was sie intern machen.
Stefan
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 4 von 7   « Erste     234 56     Letzte »    


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 16:32 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