State Machine, Visitor

Status
Nicht offen für weitere Antworten.

pat270881

Bekanntes Mitglied
Hallo,

so dann bin ich jetzt beim State. icon_confused.gif Ich habe mir wieder das Beispiel auf der einen Seite dazu angesehen mit den Kontoeinzahlungen und Auszahlungen bzw. auch ins Java übersetzt und in eclipse laufen lassen.

Meiner Meinung nach beruhen viele Design Patterns auf dem selben Prinzip. Es werden immer generalisierte Objekte definiert in denen sich dann Objekte von konkreten Klassen befinden. Somit kann ich dann immer eine Unterklassen hinzufügen und es ändert sich nur das new SilverState zb. wenn ich diese Klasse austausche. Wie in diesem Beispiel State state = new SilverState(0.0, this);

In dem Pattern gehts darum, dass ich in meiner Kontextklasse (Account) nur (in diesem Beispiel nur eines) ein Objekt von der Oberklasse State definiere, aber in diesem Objekt ein Objekt einer konkreten Unterklasse - SilverState erzeuge. Dadurch wird das Konto mit Anfangswerten initialisiert. Die Klasse State definiert auch die abstrakten Methoden für das abheben, einzahlen, etc. Das heißt, wenn man jetzt zB. was vom Konto behebt wird in der Methode Deposit mit dem generalisierten state-Objekt die Deposit Methode aufgerufen und jenachdem welches Objekt sich gerade in dem state-Objekt befindet (SilverState, RedState, GoldState) wird die entsprechende Methode dazu aufgerufen. Dann wird der neue Kontostand im StateChangeCheck berechnet, falls sich das Konto jetzt zB. im negativen Bereich befindet, wird ein neues Objekt der konkreten Unterklasse RedState erzeugt und das generalisierte State Objekt state erzählt eine Referenz auf dieses Objekt und somit wird bei einer neuerlichen Kontobewegung die Methoden der RedState Klasse verwendet.

Vorteile aus Sicht der Wiederverwendung wäre für mich hier: Man kann beliebige Klassen von Zuständen hinzufügen - solange sie die Methoden der generalisierten State Klasse implementieren können sie von der Context Klasse in Anspruch genommen werden ohne das sonst was am Code geändert werden muss. Klassen können auch beliebig ausgetauscht werden, jedoch muss ich das bei der Objekt-erzeugung in den anderen Klassen beachten, falls sich die Klassennamen ändern. (Also wenn ich zB. jetzt SilverState Klasse gegen GreenState Klasse austausche, dann muss ich auch die Objekterzeugungen zB. in der RedState Klasse, wo ja ein SilverState Objekt erzeugt wird entsprechend in GreenState umbenennen.

Was sagst du bzw. ihr dazu? - Habe ich das alles richtig verstanden, gibts sonst noch konkrete Vorteile, die dieses Pattern aufweise oder irgendwelche Hintergründe die ich noch nicht angesprochen habe? bahnhof.gif Bzw. konkrete real-world Anwendungen?

lg
patrick


Reply by SnoopP

Nunja.. die Wiederverwendung beim State-Pattern ist jetzt nicht soo das Thema meiner Meinung nach... - Wiederverwendung ergibt sich durch OOP natürlich per Definition icon_wink.gif - dadurch, dass in Design-Patterns wie du schon gesagt hast, immer auf übergeordnete Schnittstellen geachtet wird, kann das meiste beliebig erweitert werden...

Beim State-Pattern gehts um eine Umsetzung von Zustandsbasiertem Verhalten... insb. wenn man sog. Statecharts (Zustandsdiagramme) kennt, wäre das State-Pattern ein Ansatz (wenn auch ein sehr kostspieliger) Zustände und Zustandsübergänge in einer Programmiersprache umzusetzen... und zwar auch so, dass diese automatisch per Codegenerierung erfolgen könnte...

Aber zur Wiederverwendung - natürlich können jetzt beliebige Zustände noch hinzugefügt werden... ist also ganz prima erweiterbar...

(ich hatte übrigens gehofft, dass du deine Antwort doch in einen neuen Thread packst, damit auch andere hier nochmal reingucken können... so hat das Thema das gerade diskutiert wird nix mehr mit dem Topic-Titel zu tun!)
 

pat270881

Bekanntes Mitglied
Hi,

habe das jetzt so gelöst. :)

Ich habe mir jetzt auch zum Visitor Pattern bzw. zu dem real World Beispiel Gedanken gemacht:

Ich habe eine Klasse Employee die die ObjetStrcture darstellt, davon erzeuge ich zuerst mal ein Objekt. Dieses Objekt hat eine ArrayList wo die Employees gespeichert werden. Dann werden dieser ArrayList drei neue Angestellte hinzugefügt, dabei handelt es sich um abgeleitete Objekte von der Klasse Employee, diese ist widerum eine abgeleitete Klasse von Element. Beim hinzufügen der konkreten Angestellten-Objekte werden diese bei der Übergabe an die Attach-Methode auf das generalisierte Objekt Employee „gecastet“. Somit sind in der ArrayList lauter employee-Objekte.
Dann wird für das employees Objekt die Accept-Methode aufgerufen, zuerst mal mit einem neuen IncomeVisitor Objekt als Parameter. In der Employees Klasse (ObjectStructure) speziell in der Accept Methode werden jetzt die einzelnen employee-Objekte der ArrayList durchgegangen und dafür die Accept-Methode der employee-Klasse aufgerufen. Bei Aufruf der Accept-Methode wird das IncomeVisitor Objekt noch auf das IVisitor Objekt gecastet bzw. bei Übergabe an die Accept-Methode. In der Employee Klasse, in der Accept Methode, wird dann die visit-Methode aufgerufen vom IVisitor-Interface. Und je nachdem ob ein IncomeVisitor oder VacationVisitor die visit-Methode vom IVisitor Interface aufgerufen hat, wird dann die entsprechende visit-Methode aufgerufen – hier jetzt die des IncomeVisitor’s. Wobei beim Methodenaufruf der visit-Klasse das betreffene employee-Objekt in ein Element gecastet wird. Und dann in der Visit-Methode des IncomeVisitor wieder in ein employee Objekt. Das Einkommen berechnet und ausgegeben.

Soweit so gut. Das real-World Beispiel stimmt mit dem UML Diagram nicht ganz überein. Denn bei diesem Beispiel unterteilt sich die Ebene von den ConcreteElements noch mal. Es gibt hier die generalisierte Klasse Employee und davon werden dann die konkreten Elemente abgeleitet. Employee implementiert dann das Interface Element.

Das heißt wir haben jetzt eine eine ObjectStructure Klasse die eine ArrayList hat mit Employee-Objekte – bzw. darin konkrete Angestellte gefüllt.
Gut, dann verfügen die ObjectStructure-Klasse über eine Accept-Methode, womit dann ein Visitor mit dem entsprechenden employee-Objekt Operationen ausführen kann – hier beim IncomeVisitor werden einfach das Einkommen, Name, etc. des employee ausgegeben.

Was ist nun der Vorteil dieser Struktur aus Wiederverwendungssicht: Durch diese Struktur kann ich beliebige andere konkrete Angestellte-Klassen abgeleitet von der employee-Klasse hinzufügen. Dafür brauch ich dann nur in der ObjectStructure Klasse ein neues Objekt der Klasse erzeugen und der ArrayList der employees hinzufügen. Somit wird diese dann auch automatisch von den beiden Visitoren bearbeitet. Es können auch neben der Employee Klasse weitere ConcreteElement Klassen einfach hinzugefügt werden, sie müssen nur die Accept Methode des Element-Interfaces implementieren. Weiters können auch ganz einfach, weitere Visitor-Klassen hinzugefügt werden, diese müssen nur das Visitor-Interface bzw. deren Visit-Operation implementieren. So können auch leicht weitere Funktionalität hinzugefügt werden – also andere Dinge, die mit den employee-Objekten durchgeführt werden sollen.

Was meinst du bzw. ihr zu diesem Pattern?

Lg
Patrick

PS: Warum sind in diesem real-World Beispiel jetzt IVisitor und Element Interfaces und keine abstrakten Klassen?
 

SnooP

Top Contributor
Hast eigentlich alles richtig erkannt ;) ... auch, dass das Diagramm mit dem Beispiel nicht ganz stimmt - allerdings kann man an der Stelle zwecks Vereinfachung und Generalisierung so viel schachteln, wie man denn gerne möchte...

zum PS: das kann man halten wie der Dachdecker - Interfaces und abstrakte Klassen unterscheiden sich häufig ja nicht wirklich... sofern keine Referenzen oder konkrete Methodenimplementierungen in den abstrakten Klassen gespeichert werden müssen, kann man stattdessen auch Interfaces nutzen, um einen entsprechenden Typ zu definieren...
In manchen Situationen empfiehlt es sich ausigiebigst von Interfaces gebrauch zu machen, da die Mehrfachvererbung in Java ja nicht funktioniert - also muss man auf Interfaces an der Stelle zurückgreifen.
 

pat270881

Bekanntes Mitglied
Hallo,

aus gegebenen Anlass, weil anscheinend bei dem real-world Beispiel des Servers etwas nicht ganz stimmt habe ich den noch genauer analysiert:

Es werden zuerst einmal zwei Investor-Objekte erzeugt, die konkrete Observer darstellen. Danach wird das IBM-Objekt erzeugt, das ein konkretes Subjekt darstellt. Die beiden Beobachter – Investor-Objekte werden in der investors ArrayList von der Stock Klasse gespeichert. Wenn jetzt der Preis vom konkreten Subject – IBM geändert wird, wird in der abstrakten Stock-Klasse auch die Notify-Methode aufgerufen. In der Notify-Methode wir dann die Update-Methode aufgerufen vom jeweiligen Investor (Observer) mit dem Stock-Objekt und dann die Daten in der Update-Methode vom konkreten Investor ausgegeben.

Der Vorteil aus Sicht der Wiederverwendung: Man kann beliebige Oberser – Investor erzeugen und der Liste von Beobachtern(investors) der Stock-Klasse (abstrakte Subject-Klasse) hinzufügen – diese müssen nur das Interface von IInvestor implementieren.

Was bringt genau die abstrakte Stock-Klasse? – es wird in der MainApp eh gleich ein konkretes Subject – IBM Objekt erzeugt, wieso definiere ich nicht gleich dort die Liste der Observer? – Es soll ja eine 1:n Beziehung definiert werden und da habe ich dann eh nur ein Subject.

Weiters denke ich dass ein Fehler in dem Beispiel aufgetreten ist – in der Notify-Methode von der Stock-Klasse sollte das ja eigentlich so lauten oder:

Code:
Foreach (IInvestor investor in investors)

oder?

Im theoretischen Beispiel des Observer wird widerum eine abstrakte Klasse und im real-world beispiel ein Interface.

Feedback von deiner Seite? - bzw. was sagst du zu meinen Fragen? :bahnhof:

lg
patrick
 

SnooP

Top Contributor
Naja - das ist alles nicht so wahnsinnig wichtig, wie das jetzt genau umgesetzt wird - ob z.B. abstrakte Klassen oder Interfaces...

der Kern des Observers ist vermutlich verstanden... es gibt Subjects die sich irgendwie verändern können - dort eingetragen wird eine Liste mit Observers, die benachrichtigt werden müssen, wenn sich was geändert hat ... in welcher Art und Weise diese dann reagieren sollen, wird in der jeweiligen update-Methode des Observers geklärt... notifyObservers iteriert also lediglich durch die Liste und ruft jedesmal update auf.

That was it... wie genau und konkret das dann umgesetzt wird, ist nicht soo wichtig...

In der Java-API gibt es für das Observer-Pattern bereits vorgefertigte Klassen und Interfaces..

die Klasse Observable ist das Subject oder Datenobjekt aus der Modellebene, welches die "hörenden" Observer benachrichtigt... entsprechende Methoden sind dort vorgegeben. Observer selbst ist nen Interface was nur die update-Methode besitzt.
 

pat270881

Bekanntes Mitglied
Naja aber ist es nicht wichtig, dass man auf IInvestor also auf das Interface castet und nicht auf die konkrete Observerklasse??? - den fehler den ich angesprochen habe.
 
Status
Nicht offen für weitere Antworten.

Ähnliche Java Themen


Oben