Delphi-PRAXiS
Seite 4 von 7   « Erste     234 56     Letzte »    

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Tutorials und Kurse (https://www.delphipraxis.net/36-tutorials-und-kurse/)
-   -   Interfaces + Factorys (https://www.delphipraxis.net/183702-interfaces-factorys.html)

stahli 1. Feb 2015 11:57

AW: Interfaces + Factorys
 
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.

Stevie 1. Feb 2015 12:00

AW: Interfaces + Factorys
 
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.

Sir Rufo 1. Feb 2015 12:08

AW: Interfaces + Factorys
 
Wenn
Delphi-Quellcode:
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
Delphi-Quellcode:
IMotorBetrieben
einen Motor voraussetzen.

Sir Rufo 1. Feb 2015 13:05

AW: Interfaces + Factorys
 
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 :stupid: 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.

OlafSt 1. Feb 2015 18:55

AW: Interfaces + Factorys
 
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.

stahli 1. Feb 2015 19:06

AW: Interfaces + Factorys
 
Den letzten Abschnitt verstehe ich auch nicht.

Dejan Vu 1. Feb 2015 19:12

AW: Interfaces + Factorys
 
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.

stahli 1. Feb 2015 22:50

AW: Interfaces + Factorys
 
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.

Jumpy 2. Feb 2015 09:51

AW: Interfaces + Factorys
 
Zitat:

Zitat von Dejan Vu (Beitrag 1288492)
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.

Stevie 2. Feb 2015 10:00

AW: Interfaces + Factorys
 
Zitat:

Zitat von Jumpy (Beitrag 1288539)
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.


Alle Zeitangaben in WEZ +1. Es ist jetzt 05:22 Uhr.
Seite 4 von 7   « Erste     234 56     Letzte »    

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