AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Tutorials Interfaces + Factorys

Interfaces + Factorys

Ein Tutorial von stahli · begonnen am 29. Jan 2015 · letzter Beitrag vom 22. Feb 2015
Antwort Antwort
Benutzerbild von Sir Rufo
Sir Rufo

Registriert seit: 5. Jan 2005
Ort: Stadthagen
9.454 Beiträge
 
Delphi 10 Seattle Enterprise
 
#1

AW: Interfaces + Factorys

  Alt 1. Feb 2015, 11:44
Den Unterschied zwischen Tanken und Aufladen habe ich nur exemplarisch ins Spiel gebracht.
Es kann ja in echten Fällen möglicherweise Unterschiede geben, die für den weiteren Verlauf relevant sind.

Das ERZEUGEN eines Autos mit einem bestimmten Motor sehe ich nicht als Problem.
Aber später müsste ich immer prüfen:
- Kann das Objekt, das ich hier habe einem Motor haben?
- Hat es tatsächlich einen Motor?
- Kann ich damit tanken und was oder an die Steckdose?

Würdest Du also meine zwei Schnittstellen IMotor (die dann eine tatsächliche Motorinstanz representiert) und eine Schnittstelle IHasMotor (oder eigentlich besser: IKannMotorHaben) (die einem Auto und Boot hinzugefügt wird, nicht aber einem Fahrrad oder Vogel) sinnvoll finden?
Das ist immer genau das Problem an diesen "an den Haaren herbeigezogenen" Beispielen, wo man Enten, Fahrräder und Autos irgendwie gleich behandeln will. Man kommt nicht so richtig ins vorne und stösst dabei auch eben auf die besagten Probleme.

Sinnvoller erscheint mir etwas wie
Delphi-Quellcode:
IMotorBetrieben = interface
  funtion GetMotor : IMotor;
end;

IMobil = interface
  procedure BewegeZu( Ort : TPoint );
end;

IKannSprechen = interface
  procedure Sage( AText : string );
end;
Mit Supports fragt man dann ab, ob dieses oder jenes Interface unterstützt wird und führt das dann aus:
Delphi-Quellcode:
procedure Sage( AText : string; AContext : IInterface );
var
  LSprecher : IKannSprechen;
begin
  if Supports( AContext, IKannSprechen, LSprecher ) then
    LSprecher.Sage( AText );
end;
Kaum macht man's richtig - schon funktioniert's
Zertifikat: Sir Rufo (Fingerprint: ‎ea 0a 4c 14 0d b6 3a a4 c1 c5 b9 dc 90 9d f0 e9 de 13 da 60)
  Mit Zitat antworten Zitat
Benutzerbild von stahli
stahli
Online

Registriert seit: 26. Nov 2003
Ort: Halle/Saale
4.358 Beiträge
 
Delphi 11 Alexandria
 
#2

AW: Interfaces + Factorys

  Alt 1. Feb 2015, 11: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.
Stahli
http://www.StahliSoft.de
---
"Jetzt muss ich seh´n, dass ich kein Denkfehler mach...!?" Dittsche (2004)
  Mit Zitat antworten Zitat
Benutzerbild von Sir Rufo
Sir Rufo

Registriert seit: 5. Jan 2005
Ort: Stadthagen
9.454 Beiträge
 
Delphi 10 Seattle Enterprise
 
#3

AW: Interfaces + Factorys

  Alt 1. Feb 2015, 12: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.
Kaum macht man's richtig - schon funktioniert's
Zertifikat: Sir Rufo (Fingerprint: ‎ea 0a 4c 14 0d b6 3a a4 c1 c5 b9 dc 90 9d f0 e9 de 13 da 60)
  Mit Zitat antworten Zitat
Benutzerbild von Sir Rufo
Sir Rufo

Registriert seit: 5. Jan 2005
Ort: Stadthagen
9.454 Beiträge
 
Delphi 10 Seattle Enterprise
 
#4

AW: Interfaces + Factorys

  Alt 1. Feb 2015, 13: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.
Kaum macht man's richtig - schon funktioniert's
Zertifikat: Sir Rufo (Fingerprint: ‎ea 0a 4c 14 0d b6 3a a4 c1 c5 b9 dc 90 9d f0 e9 de 13 da 60)
  Mit Zitat antworten Zitat
OlafSt

Registriert seit: 2. Mär 2007
Ort: Hamburg
284 Beiträge
 
Delphi 10.2 Tokyo Professional
 
#5

AW: Interfaces + Factorys

  Alt 1. Feb 2015, 18: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
Online

Registriert seit: 26. Nov 2003
Ort: Halle/Saale
4.358 Beiträge
 
Delphi 11 Alexandria
 
#6

AW: Interfaces + Factorys

  Alt 1. Feb 2015, 19:06
Den letzten Abschnitt verstehe ich auch nicht.
Stahli
http://www.StahliSoft.de
---
"Jetzt muss ich seh´n, dass ich kein Denkfehler mach...!?" Dittsche (2004)
  Mit Zitat antworten Zitat
Dejan Vu
(Gast)

n/a Beiträge
 
#7

AW: Interfaces + Factorys

  Alt 1. Feb 2015, 19: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
Online

Registriert seit: 26. Nov 2003
Ort: Halle/Saale
4.358 Beiträge
 
Delphi 11 Alexandria
 
#8

AW: Interfaces + Factorys

  Alt 1. Feb 2015, 22: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.
Stahli
http://www.StahliSoft.de
---
"Jetzt muss ich seh´n, dass ich kein Denkfehler mach...!?" Dittsche (2004)
  Mit Zitat antworten Zitat
Jumpy

Registriert seit: 9. Dez 2010
Ort: Mönchengladbach
1.740 Beiträge
 
Delphi 6 Enterprise
 
#9

AW: Interfaces + Factorys

  Alt 2. Feb 2015, 09: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

Registriert seit: 12. Aug 2003
Ort: Soest
4.052 Beiträge
 
Delphi 10.1 Berlin Enterprise
 
#10

AW: Interfaces + Factorys

  Alt 1. Feb 2015, 12: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
“Simplicity, carried to the extreme, becomes elegance.” Jon Franklin

Delphi Sorcery - DSharp - Spring4D - TestInsight
  Mit Zitat antworten Zitat
Antwort Antwort

Themen-Optionen Tutorial durchsuchen
Tutorial durchsuchen:

Erweiterte Suche
Ansicht

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 18:04 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