|
Dieses Pattern beschreibt die Realisierung einer Verarbeitungskomponente in einer Pipeline. Die Komponente bezieht ihre Daten aus einer oder mehreren Verbindungskomponenten, realisiert die Anbindung und den bedingten Aufruf einer Verarbeitungsfunktion und stellt die Daten an der Schnittstelle zu einer weiteren Verbindungskomponente zur Verfügung.
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 zur Weiterleitung der zu verarbeitenden Daten. Für die Integration der Verarbeitungskomponenten in die Pipeline bedarf es einer festen Schnittstelle sowohl für die Eingabedaten als auch für die Ausgabedaten. Damit die Komponente einen rein strukturierenden Charakter erhält, ist es notwendig die Berechnungskomponente auszulagern und den Frameworkcharakter der Pipelinestruktur durch eine feste Schnittstelle zur Berechnungskomponente zu betonen. Weiterhin muß festgelegt werden, was passieren soll, wenn das Ergebnis berechnet werden soll, oder wenn Daten angeliefert werden.
Immer wenn ein neuer Attribut-Wert auf der Basis anderer Werte berechnet werden soll, kann dieses Pattern eingesetzt werden. Es erlaubt unter anderem den Bezug auf ein Standard-Interface für Patterns, die Berechnungsfunktionen realisieren. Richtig Sinn macht dieses Pattern aber erst, wenn mehrere Filterkomponenten zu einer Pipeline verschaltet werden sollen. Es lassen sich dann Constraints beschreiben, die den Datenfluß steuern.
Ein Filter wird für ein Objekt target realisiert (siehe Abbildung). Es implementiert bei Bedarf für eingehende Verbindungskomponenten eine Methode notify . Für die Implementierung der Methode arguments werden Bindungen an Zugriffsmethoden destData oder Methoden pull der eingehenden Verbindungskomponenten benötigt. Bei Bedarf wird eine Methode request für rausgehende Verbindungskomponenten implementiert und eine Methode push benutzt.

Hier sind die Zugriffsmethoden der Attribute destData oder die Methoden pull von den beteiligten Pipes zu binden.
Liefert ein Array mit den Argumenten für die Berechnungsfunktion des Filters.
Diese Methode wird von dem Pattern zur Verfügung gestellt, das die Berechnungsfunktion realisiert.
Triggert die Berechnung der Filterfunktion. Diese Methode wird vom Filter-Pattern realisiert. An dieser Stele können zum Beispiel Activity-Patterns ( ContinuousComputation) angebunden werden.
Diese Methode wird optional vom Filter -Pattern implementiert und kann von einer Pipe-Komponente dazu genutzt werden, die Filter-Komponente über die Änderung eines Argumentwertes zu informieren. Dadurch wird unter Umständen eine Neuberechnung getriggert.
Fordert die Filter-Komponente auf, die Daten zur Verfügung zu stellen. Unter Umständen wird eine Neuberechnung durchgeführt.
Propagiert das Ergebnis von der Attribut-Variable result in die Pipe. Diese Methode wird vom Pipe -Pattern zur Verfügung gestellt.
Dieser boolsche Wert bestimmt, ob mit einer SelectivePipe zusammen gearbeitet werden soll, oder nicht. Die SelectivePipe behebt das in [Kae98] auf Seite 41 beschriebene Problem. Für das betreffende Pipe muß selectivePipe ebenfalls definiert werden. selectivePipe ist nur für PullActive-Pipes sinnvoll.
Dieses Pattern arbeitet eng mit dem Pattern Pipe , sowie den Patterns für die Berechnung domänenspezifischer Funktionen wie ThermalMass , ThermalJunction , u.ä. zusammen. Das Pipe-Pattern am Ausgang des Filters benötigt, je nach Konfiguration der Pipeline, eine Bindung für die Methode request . Es stellt unter Umständen die Methode push zur Verfügung. Wird die Methode request gebunden, dann triggert diese die Berechnung des zu transportierenden Wertes (calculateOnPull ). Wird push gebunden, dann propagiert das Filter automatisch das Ergebnis nach einer Berechnung (propagateAfterCalculation ).
Die Pipes an den Eingängen des Filters benötigen, je nach Konfiguration der Pipeline, Bindungen für notify . Das Filter benötigt Bindungen für arguments . Wird notify gebunden, dann wird zunächst das neue Argument geholt, und dannach eine neue Berechnung angestoßen (calculateOnPush ). Die Methodenliste arguments kann entweder an die destData -Zugriffsmethoden oder die pull -Methoden der Pipes gebunden werden. Gemischte Bindungen sind möglich. Eine Bindung an pull fordert erst einen neuen Wert an, bevor die Berechnung durchgeführt wird (pullOnCalculate ).
Die Zusammenarbeit zwischen den Pipes und dem Filter bezüglich des Synchronisationsverhaltens muß also abgestimmt werden.
Die Patterns, die eine Simulationfunktion realisieren, benötigen Argumente und liefern ein Ergebnis. Das Filter-Pattern implementiert die Methode getArguments . Sie liefert ein Array mit allen Argumenten. Das Ergebnis wird in das Attribut result geschrieben werden. Der Name der Methode, die die Berechnung ausführt, muß dem Filter-Pattern bekannt gemacht werden (calculate ).
Der durch Pipe und Filter realisierte Datenfluß ist vom Typ der Daten unabhängig. Ob die transportierten Daten mit den gewünschten Funktionen verarbeitbar sind, liegt in der Verantwortung des Entwerfers.
Die hier aufgeführten Code-Templates stellen nur Skizzen dar. Sie korrespondieren mit den Implementierungsbeispielen des Pipe-Patterns. Der Fall selectivePipe ist nicht berücksichtigt.
"Liefert ein Array mit den Argumenten fuer die Berechnung." | anArray | anArray := Array new: n. anArray at: 1 put: self {pullOrGet1}. anArray at: 2 put: self {pullOrGet2}. ... anArray at: n put: self {pullOrGetn}. ^ anArray
" Fuehrt die Berechnung durch und schreibt das Ergebnis nach {result}" self {result}: self {calculate} ^ self {push}
Wurde push nicht gebunden, dann entfällt die letzte Zeile. Das Ergebnis wird dann nicht automatisch propagiert.
"Eines der Argument wurde geaendert, hier erfolgt daraufhin eine Neuberechnung von {result}." ^ self {compute}
"{result} wird fuer die Berechnung in einer folgenden Berechnungsstufe benoetigt, hier erfolgt daraufhin eine Neuberechnung von {result}. " ^ self {compute}
Besonders die letzten beiden Methoden sind nicht besonders komplex. Es sind aber trotzdem verschiedene Methoden für ein korrektes Handshaking notwendig, denn es wird aufgrund von vorhandenen Bindungen entschieden, wie sich das Filter verhalten soll. Sollen von Filter mal spezialisiertere Patterns abgeleitet werden, z.B. ein CachedFilter, der die Berechnungen nur vornimmt, wenn sich auch wirklich Argumente seit der letzten Berechnung verändert haben, dann ist die Unterscheidung auf jeden Fall notwendig.
Unter Umständen ist ein Attribut result in der Implementierung auch gar nicht notwendig oder wünschenswert, so daß PSiGene das Berechnungsergebnis direkt weitergeben kann.
Pipes and Filters aus [BMR96] ( http://wwwagz.informatik.uni-kl.de/projects/SFB501/D1/PSiGene/publications.shtml#BMR96 ).
Pattern Filter Category paf ObjectType use target Attribute implement result at target ComplexMethod use arguments at target SingleMethod implement getArguments at target SingleMethod use calculate at target SingleMethod implement preset compute at target SingleMethod implement optional request at target SingleMethod implement optional notify at target SingleMethod use optional push End