Einzelnen Beitrag anzeigen

choose

Registriert seit: 2. Nov 2003
Ort: Bei Kiel, SH
729 Beiträge
 
Delphi 2006 Architect
 
#18

Re: Zugriffverletzung beim Verwenden von Interfaces

  Alt 16. Dez 2003, 11:07
Dann also kein neuer Thread, aber ordentlich lange Beiträge

Zitat:
1.) jeder Stream kann intern auf einen einzigsten verlinkten Stream verweisen.
2.) besondere Streams arbeiten als Multiplexer/Verteiler per Listen von mehreren Streams, diese Verteiler/Multiplexer arbeiten im In/Output wie ein einzigster Stream, geben/holen aber ihren In/Output an mehrere Streams weiter.
Das sind Möglichkeiten, das Decorator-Pattern anzuwenden. Beides geeignet, allerdings stimme ich 1) nicht zu. Schließlich sind FileInputStream, SocketOutputStream oder ArrayInputStream die jeweiligen Enden dieser Ketten und benötigen deshalb keine weiteren Referenzen.
Innerhalb einer Hierarchie sollte es deswegen meiner Meinung nach eine Abstract InputStream klasse geben, von Denen zum einen die DecoratorStreams bzw die FilterStreams (ich verwende im Folgenden die letztere Bezeichnung) erben, sowie die Tatsächlichen Eingabeströme (so) und die von Dir vorgeschlagenen Multiplexer etc., im Folgenden als Kompositum bezeichnet, abgeleitet werden.
In der beispielhaften Hierarchie (Abb.1) wird der "lesende Zweig" der Stream-Hierarchie mit der abstrakten Klasse Stream beschrieben, die Methoden zum Schließen (Close) und zum überspringen von Datenworten (ich verwende der Einfachheit halber Bytes) mithilfe der Methode Skip(...) anbietet. Von ihr Erbt die abstrakte Klasse TInputStream. Sie beschreibt die Signatur eines Eingabestreams mit den beiden zusätzlichen Methoden Read und IsEOF. Basierend auf dieser Signatur zeigt das UML-Diagram weiterhin die bereits erwähnten beispielhaften Klassen.
Anmerken möchte ich weiterhin, dass die Beiden Interfaces IStream und IInputStream den von Dir geforderten Interfaces zur Bearbeitung von abstrakten Datenquellen entsprechen, sie werden von den beiden abstrakten Oberklassen TStream bzw TInputStream implementiert (die Ermittlung der Chunkgröße habe ich aus Verfachungsgründen vernachlässigt).

Existiere zu jeder Klasse eine entsprechende Konstruktionsfunktion (auch als jew Klassenmethode denkbar), die statt des Exemplars eine Referenz auf das Interface zurückgibt, könnte man so eine Konstruktion zum einlesen einer vershlüsselten Datei so erreichen:
myStream:= DecryptInputStream(BufferedInputStream(FileInputStream('myFile'), 4096)); und mit eine Schleife der Form
Delphi-Quellcode:
while not myStream.IsEOF do
  DoSth(myStream.Read);
gepuffterte Daten aus einer Datei entschlüsseln und an eine Methode DoSth(...) übergeben.

Zitat:
Allerdings entsteht nun ein neues Problem: wie wird es fertiggebracht das jeder Stream am Ende einer Transaktion noch zusätzliche Daten anhängen bzw. entfernen kann ?
Wenn ich Deine Darstellung richtig verstanden habe, sollten sich diese Daten als "normale Daten des Stroms" vollkommen Transparent einarbeiten lassen, weil die jeweiligen Header- und Footer-Informationen nur für den jeweiligen Gegenpart (InputStream vs. OutputStream) von Bedeutung ist. Ein Pufferstream sollte deshalb ebenfalls diese Daten Puffern und braucht keine Kenntnis über die "Art der Information" zu besitzen.

Um meine Lösungsidee hierzu beschreiben zu können, möchte ich zunächst die Klasse TFiFoStream einführen, die sowohl das bereits dargestellte Interface IInputStream als auch das komplementäre Interface IOutputStream (mit den beiden Methoden Write(...) und Flush) implementiert und als FiFo (First in First Out) Puffer fungiert. Die in ein Exemplar dieser Klasse geschriebenen Daten mithilfe des durch IOutputStream geforderten Methoden können demnach anschließend über die Methoden des Interfaces IInputStream gelesen werden (TFiFoPuffer sollte nicht in die beschriebene Klassen-Hierarchie eingearbeitet werden sondern gesondert mit einer aggregierten Referenzzählung (siehe D7 TAggregatedObject) implementiert werden).

Unter Verwendung eines aggregierten Exemplars dieser Klasse kann TInputStream die drei Template-Methoden (GoF) BeforeRead(...), DoRead(...) und AfterRead(...) einführen und das Ermitteln des nächsten Datenabschnitts in Abhängigkeit des internen Status an die jeweilige Methode delegieren:
Delphi-Quellcode:
function TInputStream.Read: Byte;
begin
  // no more byte available -> exception
  if IsEOF then
    raise E...

  
  Result:= FiFoStream.Read;
end;

function TInputStream.IsEOF: Boolean;
begin
  // ensure next byte and State is prepared
  PrepareRead;

  Result:= FiFoStream.IsEOF;
end;

function TInputStream.PrepareRead;
begin
  // only read if no byte available in fifo and stream not closed
  if FiFoStream.IsEOF and (State<ssClosed) then
  repeat
    // do state specific action
    case State of
      ssBefore: BeforeRead(FiFoStream);
      ssReading: DoRead(FiFoStream);
      ssAfter: AfterRead(FiFoStream);
    end;

    // go to next state
    if (State in [ssBefore, ssAfter]) or (FiFoStream.IsEOF) then
      FState:= Succ(State);

   // loop until at least one byte available in fifo or no more byte
  until not FiFoStream.IsEOF or (State=ssClosed);
end;
Wie man erkennen kann, ist die Implementierung der Templatethoden nun relativ einfach in der Form
Delphi-Quellcode:
function TInputStream.BeforeRead(AnOutputStream: IOutputStream);
begin
  AnOutputStream.Write(SomeHeaderData);
end;
realisierbar und auch auch das Byteweise lesen oder Verarbeiten in DoRead(...) stellt kein Problem dar, weil der Status in PrepareRead erst dann auf ssAfter gesetzt wird, sobald keine weiteren Bytes in DoRead(...)
an den FiFo-Puffer übergeben worden.

Fortsetzung folgt...
Miniaturansicht angehängter Grafiken
abb1inputstreams.png  
gruß, choose
  Mit Zitat antworten Zitat