|
Dieses Pattern beschreibt die Realisierung einer Verbindungskomponente zwischen zwei Verarbeitungskomponenten, die den gerichteten Datenaustausch zum Ziel hat, z.B. zwei Filtern, einem Filter und einer Senke, oder einer Quelle und einem Filter. Weiterhin übernimmt diese Verbindungskomponente die Synchronisierung von aktiven Verarbeitungskomponenten.
Pipes and Filters by Buschmann et al ( http://wwwagz.informatik.uni-kl.de/projects/SFB501/D1/PSiGene/publications.shtml#BMR96 ).
Eine Pipeline besteht aus Komponenten zur Verarbeitung von Daten und aus Komponenten für die Weiterleitung der zu verarbeitenden Daten. Um vom Transportmechanismus abstrahieren zu können, soll dieses Pattern eine Komponente realisieren, welche an einem fest definierten Interface
einen Transportdienst realisiert. Ableitungen von diesem Pattern, Properties oder Erkenntnisse aus dem Kontext können zu verschiedenen Implementierungen der Komponente führen, was die umgebenen Komponenten allerdings in keiner Weise beeinflußt. Der Einsatz dieses Patterns soll es dem Entwerfer erlauben sich auf den Datenfluß eines Systems zu konzentieren.
Immer wenn ein Attribut eines Objektes an den Wert eines Attributes eines anderen Objektes gebunden werden soll und der Notwendigkeit der Abstraktion vom Transportweg besteht, kann dieses Pattern eingesetzt werden. Es realisiert einen transparenten Transportweg, dessen Synchronisationsverhalten vielfältig beeinflußbar ist.
Eine Pipe wird zwischen zwei Objekten realisiert, der source und der destination . Dabei wird das Attribut sourceData and das Attribut destData gebunden, siehe dazu auch die folgende Abbildung . Die beiden Objekte sind über eine 1:1-Beziehung miteinander verbunden.
Das dynamische Verhalten der beteiligten Objekte ist ebenfalls in der Abbildung skizziert. Es werden 4 mögliche Realisierungen unterschieden, die auf unterschiedliche Kontexte zurückzuführen sind.
Die letzten drei Fälle ermöglichen zusätzlich die Pufferung der
zu übertragenden Daten. Dieser buffer wird realisiert sofern eine Puffergröße bufferSize größer Null spezifiziert wird. Die Aufrufhierarchie der Methoden ist in der
Abbildung durch Pfeile dargestellt.


Ist dieser Wert definiert und größer Null, dann wird auf der Ausgangsseite der Pipe ein Puffer mit der angegebenen Größe in Form einer FIFO realisiert.
Dieser boolsche Wert bestimmt, ob eine SelectivePipe erzeugt werden soll, oder nicht. Die SelectivePipe behebt das in [Kae98] auf Seite 41 beschriebene Problem. Für das betreffende Filter muß selectivePipe ebenfalls definiert werden. selectivePipe ist nur für PullActive-Pipes sinnvoll.
Ein Wert source oder destination fordert die Aggregation eines AttributeProxy -Patterns für einen der beiden Objekttypen. Die Implementierung Pipe wird dann auf den Umgang mit den Proxies abgestimmt.
Das Pattern arbeitet eng mit dem Pattern Filter zusammen. Durch Kombination lassen sich aus den beiden Patterns komplexe Pipelines zusamenstellen. Das Pattern Pipe benötigt unter Umständen Implementierungen für die Methoden notify und request durch die drei genannten Patterns. Es stellt diesen Patterns Implementierungen für die Methoden read , push und pull zur Verfügung. Es stellt ein Attribut destData zur Verfügung, welches den Wert des Attributs sourceData wiederspiegelt.
Das Pattern aggregiert je nach Konstellation die Patterns SingleIndirection (obsolete) , MethodAlias , AttributeValue , BufferedValue und AttributeProxy .
Gemäß den 4 unterschiedlichen Implementierungen für eine Pipe aggregiert dieses Pattern andere Patterns oder erzeugt Methoden auf der Basis von Code-Templates. Die Variante ohne jegliche Aktivität der benachbarten Objekte kann durch einfache Aggregation des Patterns FollowRelation realisiert werden. Für die Variante Push+Pull-Activity ohne selectivePipe sieht der teilweise aggregierte Code wie folgt aus:
"Fordert die Daten bei {source} an. Diese stehen dann in {destData} zur Verfuegung." (self {entry} instance {request}) ifTrue: [self {read}]. ^ self {destData}
Je nachdem, ob es sich bei der verbindenden Relation um eine 1:1 oder 1:n-Relation handelt, wird die erste, oder die zweite Variante für push erzeugt.
"Fordert die nachfolgende Verarbeitungskomponente auf, die Daten bei source abzuholen." ^ self {exit} instance {notify}
"Fordert die nachfolgende Verarbeitungskomponenten auf, die Daten bei source an abzuholen." ^ self {exit} connections collect: [:each | each {notify}]
"Liest die Daten aus sourceData bei source und stellt sie dann in {destData} zur Verfuegung." ^ self {semaphore} critical: [| temp | temp := self {entry} instance {sourceData}. (temp == self {popDestData}) ifTrue: [false] ifFalse: [self {destData}: temp. true]]
Für destData wird je nachdem, ob bufferSize gesetzt ist, oder nicht, das Pattern BufferedValue oder AttributeVariable aggregiert. Im ersten Fall, wird die Bindung für popDestData auf "pop{destData}" gesetzt. Im letzten Fall werden die beiden Bindungen gleichgesetzt. Für semaphore wird ein AttributeVariable aggregiert. Diese Variable wird mit einem Semaphore aus dem Smalltalk-Runtime initialisiert.
Pipes and Filters aus [BMR96] ( http://wwwagz.informatik.uni-kl.de/projects/SFB501/D1/PSiGene/publications.shtml#BMR96 ), Filter
Pattern Pipe Category paf ObjectType use source ObjectType use destination Attribute use sourceData at source Attribute implement destinationData at destination SingleRelation use exit at source SingleRelation use entry at destination SingleMethod use optional request at source SingleMethod implement optional read at destination SingleMethod use optional notify at destination SingleMethod implement optional pull at destination SingleMethod implement optional push at source Expression implement preset bufferSize Expression implement preset selectivePipe Expression implement preset useProxy End