|
|
|
|
|
Nachdem es dem Kunden möglich ist, das Angebot zu
betrachten, soll er auch Videos ausleihen können. Dazu
muß sich der Kunde am Automaten mit seiner Kundennummer
identifizieren, sich Videos aussuchen und den entsprechenden
Preis bezahlen.
|
|
|
Vorgänge dieser Art werden im Framework von
Prozessen bearbeitet. Ein Prozeß kann
benutzt werden, um beliebige Datenstrukturen zu
manipulieren. Derartige Datenstrukturen können
u.a. Objekte der vom Framework bereitgestellten Datentypen
DataBasket , Stock und Catalog
sein.
|
|
|
Prozesse werden im Framework von der Klasse SaleProcess implementiert. Sie werden als
endliche deterministische Automaten verstanden,
deren Zustandsübergänge sowohl durch
Benutzereingaben, als auch spontan ausgelöst werden
können. Die Zustände werden durch
Objekte vom Typ Gate , die
Zustandsübergänge durch Objekte vom Typ Transition realisiert.
|
|
|
Eine Transition wird als eine
unteilbare (atomare) Operation abgearbeitet, sie wird vom
Framework nicht unterbrochen. Daher darf es bei der
Abarbeitung einer Transition auf keinen
Fall zu einer Kommunikation mit dem Benutzer oder irgendeiner
anderen langwierigen, möglicherweise endlosen Aktion
kommen.
|
|
|
Im Gegensatz zu Transitions dürfen
Gates unterbrochen werden (signalisiert
durch eine InterruptedException ). Für die
Nutzerinteraktion gibt es eine spezielle Art von Gates , die UIGates .
Es ist vorgesehen, an ihm ein FormSheet
oder ein MenuSheet (oder beides) zu
setzen. Die Entscheidung, welche der von einem UIGate wegführenden Transitionen auszuwählen ist, hängt
von der Benutzereingabe ab.
|
|
|
Das Prozeßmodell ist geeignet, selbst komplizierte
Abläufe zu planen und umzusetzen.
|
|
|
Für den Ausleihvorgang werden fünf Gates zusätzlich zu den vom Framework
bereits zur Verfügung gestellten benötigt: eines, an
dem sich der Kunde anmeldet, ein zweites, an dem er die Videos
auswählt und ein drittes, an dem die Videos bezahlt
werden. Zwei weitere Gates werden
benötigt, um zu entscheiden, ob genug Geld bezahlt wurde
und ggf. Wechselgeld zu geben.
|
|
|
Außerdem werden vier nichttriviale Transitions benötigt.
"Nichttrivial" heißt, es kann nicht einfach
von einem Gate zum nächsten
gewechselt werden, sondern es ist zusätzlich das
nächste Gate vorzubereiten.
|
|
|
Die Zustände und Zustandsübergänge des
Automaten werden in der folgenden Abbildung noch einmal
verdeutlicht:
Abbildung 2.1:
Der Ausleihvorgang als deterministischer Automat
|
|
|
Zunächst jedoch muß der neue SaleProcess angelegt werden. Alle, im Laufe
der Implementation wichtigen, import -Anweisungen
werden sofort hinzugefügt, da inzwischen klar sein
sollte, welche Framework-Pakete für welche Anweisungen
benötigt werden.
|
|
|
RentProcess.java |
|
|
| | |
|
import sale.*;
import sale.stdforms.*;
import data.*;
import data.ooimpl.*;
import data.stdforms.*;
import users.*;
import java.util.*;
import java.text.*;
import java.lang.*;
public class RentProcess extends SaleProcess
{
}
| |
| | |
|
|
|
Im Konstruktor wird lediglich der Konstruktor der Oberklasse
aufgerufen. Ihm wird der Name für den Prozeß
übergeben:
|
|
|
| | |
|
public RentProcess()
{
super("RentProcess");
}
| |
| | |
|
|
|
Außerdem sind die Gates und
Transitions zu deklarieren:
|
|
|
| | |
|
protected UIGate capabilityGate;
protected UIGate selectionGate;
protected UIGate rentGate;
protected Gate decisionGate;
protected UIGate getChangeGate;
protected Transition toSelectionTransition;
protected Transition toPayingTransition;
protected Transition toDecisionTransition;
protected Transition toGetChangeTransition;
| |
| | |
|
|
|
Es werden weitere Variablen benötigt, die für den
Ablauf des Prozesses wichtige Werte enthalten. Diese sind die
zu verwendende Währung, der zu zahlende Betrag, der
gezahlte Betrag und ein Wert, der aussagt, wie die beiden
Beträge in Relation zueinander stehen. Außerdem
wird noch eine Variable für den Kunden benötigt, der
Videos ausleihen möchte und eine weitere für einen
Iterator über die Liste der auszuleihenden Videos. Dieser
muß transient sein, weil sonst beim Abspeichern der
SaleProcess eine Exception auslöst.
|
|
|
| | |
|
private Currency myCurrency;
private IntegerValue toPayValue;
private IntegerValue paidValue;
private int payAssessment;
private Customer customer;
private transient Iterator videoIterator;
| |
| | |
|
|
|
Um den Automaten zum gegebenen Zeitpunkt korrekt und einfach
aufzubauen, wird eine Methode setupMachine
implementiert, die diese Arbeit übernimmt:
|
|
|
| | |
|
protected void setupMachine()
{
}
| |
| | |
|
|
|
In dieser Methode wird zunächst die Variable
myCurrency mit der zu verwendenden Währung
belegt. Dies geschieht vornehmlich aus Gründen der
Effizienz und der Übersichtlichkeit - auf diese Weise
muß nicht jedesmal erst der Shop
und von diesem der entsprechende Catalog geholt werden.
|
|
|
| | |
|
myCurrency = (Currency)Shop.getTheShop().getCatalog("EURO");
| |
| | |
|
|
|
Der derzeit gültige DataBasket
wird ermittelt. Ebenfalls muss der zu verwendende Bestand der
Videos ermittelt werden:
|
|
|
| | |
|
final DataBasket db = getBasket();
final CountingStockImpl cs =
(CountingStockImpl)Shop.getTheShop().getStock("Video-Countingstock");
| |
| | |
|
|
|
Nun sollen die UIGates in der Methode
setupMachine angelegt werden. Zunächst das,
an dem sich die Kunden anmelden.
|
|
|
Das Capability-Gate stellt den
Startzustand des Automaten dar. Es wird keine Transition geben, die zu ihm hin führt,
deshalb wird das FormSheet mir Hilfe
eines FormSheetContentCreators erstellt
und direkt übergeben. Alles, was zur Abarbeitung des
Gates nötig ist, muss also vor dem
Starten des Automaten erledigt werden. Da die Methode
setupMachine vor dem Starten ausgeführt
werden soll, um den Automaten überhaupt erst
vorzubereiten, ist hier die geeignete Gelegenheit zur
Vorbereitung des Gates . In diesem Fall
wird ein FormSheet zur Eingabe der
Kundennummer gesetzt. Dazu wird ein TextInputForm verwendet. Dies ist ein
FormSheet , das ein Label und eine
Eingabezeile vom Typ JTextInput enthält.
|
|
|
| | |
|
final TextInputForm tif =
new TextInputForm("Customer-ID", "Customer-ID", "");
| |
| | |
|
|
|
Zur Ausgestaltung des FormSheets wird
der bereits bekannte FormSheetContentCreator erstellt, um bei
einem möglichen Abspeichern und erneutem Laden das FormSheet wiederherstellen zu können:
|
|
|
| | |
|
tif.addContentCreator(new FormSheetContentCreator()
{
protected void createFormSheetContent(FormSheet fs)
{
}
});
| |
| | |
|
|
|
Nun wird der Standard "Ok"-Button mit einer Action versehen, die als anonyme Klasse
implementiert ist:
|
|
|
| | |
|
fs.getButton(FormSheet.BTNID_OK).setAction( new sale.Action()
{
});
| |
| | |
|
|
|
Die nach dem Betätigen des Buttons auszuführende
Aktion besteht darin, bei falscher Kundennummer die Eingabe
wiederholen zu lassen. Ist die Kundennummer jedoch ein
Integer , wird ggf. ein neuer Kunde erzeugt und
dann zum selectionGate gewechselt. In die Action wird folgende
doAction -Methode eingefügt:
|
|
|
| | |
|
public void doAction(SaleProcess p, SalesPoint sp)
{
String customerID = tif.getText();
boolean isNotAnInteger = true;
try {
new Integer(customerID);
isNotAnInteger = false;
}
catch(NumberFormatException nfe) {
isNotAnInteger = true;
}
if(!isNotAnInteger
&& (new Integer(customerID)).intValue() > 0) {
try {
customer = new Customer(customerID);
VideoMachine.addCustomer(customer);
}
catch (DuplicateKeyException dke) {
customer = VideoMachine.getCustomerByID(customerID);
}
capabilityGate.setNextTransition(toSelectionTransition);
}
else {
JOptionPane.showMessageDialog(
null,
"CustomerID must be a positive Number!");
capabilityGate.setNextTransition(
new GateChangeTransition(capabilityGate));
}
}
| |
| | |
|
|
|
Für JOptionPane.showMessageDialog() ist die
folgende import -Anweisung nötig:
|
|
|
| | |
|
import javax.swing.JOptionPane;
| |
| | |
|
|
|
Es fehlt nun noch die Action für
den "Cancel"-Button, der zum Rollback-Gate führt:
|
|
|
| | |
|
fs.getButton(FormSheet.BTNID_CANCEL).setAction( new sale.Action()
{
public void doAction(SaleProcess s, SalesPoint sp)
{
capabilityGate.setNextTransition(
GateChangeTransition.CHANGE_TO_ROLLBACK_GATE);
}
});
| |
| | |
|
|
|
Der FormSheetContentCreator ist
abgeschlossen und das Gate kann jetzt
mit dem FormSheet initialisiert
werden. Da kein MenuSheet benötigt
wird, wird dieses null gesetzt.
|
|
|
| | |
|
capabilityGate = new UIGate((FormSheet) tif, (MenuSheet) null);
| |
| | |
|
|
|
Die weiteren Gates müssen
entsprechend durch den Verlauf beeinflusst werden. Dies wird
verwirklicht, indem man statt der Gates
die entsprechenden Transitions benutzt,
die zu den Gates führen. Die Transition bereitet das Gate dann während der Laufzeit
vor. Zunächst initialisieren wir das Gate , wobei das FormSheet hier ebenfalls null
gesetzt wird. Dieses wird erst während der Laufzeit durch
die Transition erstellt.
|
|
|
| | |
|
selectionGate = new UIGate((FormSheet) null, (MenuSheet) null);
| |
| | |
|
|
|
Danach implementieren wir die
toSelectionTransition , die zum
SelectionGate führen soll und in dem die zu
leihenden Videos ausgewählt werden sollen:
|
|
|
| | |
|
toSelectionTransition = new Transition()
{
};
| |
| | |
|
|
|
Wie man sieht, wird die Transition als
anonyme Klasse realisiert. Dies wird auch mit allen weiteren
Transitions geschehen. In der anonymen
Klasse ist die perform -Methode zu
implementieren:
|
|
|
| | |
|
public Gate perform(SaleProcess pOwner, User usr)
{
}
| |
| | |
|
|
|
Diese Methode ist in einer Transition
dafür verantwortlich, alle nötigen Arbeiten
auszuführen, die bis zum Erreichen des nächsten
Gates nötig sind. Dazu gehört
auch das Vorbereiten des nächsten Gates selbst. In diesem Fall führt die
Transition von der erfolgreichen
Eingabe der Kundennummer (capabilityGate ) zu dem
Gate , an dem die Auswahl der Videos
erfolgen soll (selectionGate ).
|
|
|
Jetzt füllen wir die Methode perform() mit
Leben. Dazu wird zuerst das Betreten des
selectionGate vorbereitet. Dazu wird das FormSheet erzeugt, das beim Betreten des
selectionGate angezeigt werden soll:
|
|
|
| | |
|
TwoTableFormSheet ttfs =
TwoTableFormSheet.create("Make your Selection",
cs,
db,
selectionGate,
null,
null,
false,
new OfferTED(true),
null,
null);
| |
| | |
|
|
|
Wie man sieht, besitzt auch TwoTableFormSheet eine Reihe von create -Methoden für viele Kombinationen
von Parametern. An dieser Stelle wurde die Methode mit
folgenden Parametern verwendet:
- Titel des
FormSheets
- Quelle, hier der
CountingStock
- Ziel, hier der
DataBasket
-
UIGate , an dem das
FormSheet darzustellen ist
-
Comparator für die Quelltabelle
-
Comparator für die Zieltabelle
- Aussage, ob die Zeilen mit dem Wert 0 in der
Quelltabelle gezeigt werden sollen
-
TableEntryDescriptor der
Quelltabelle
-
TableEntryDescriptor der
Zieltabelle
- Strategie für die Bewegung der Einträge
|
|
|
Als nächstes muß das FormSheet den Bedürfnissen des
selectionGate angepaßt werden. Dazu wird
hinter der Erzeugung des FormSheets ein
FormSheetContentCreator
eingefügt. Auch er wird als anonyme Klasse implementiert:
|
|
|
| | |
|
ttfs.addContentCreator(new FormSheetContentCreator()
{
});
| |
| | |
|
|
|
Im ContentCreator muß nun eine Methode
createFormSheetContent implementiert werden. Sie
wird bei der Erstellung des FormSheets
aufgerufen, um die notwendigen Anpassungen vorzunehmen.
|
|
|
| | |
|
protected void createFormSheetContent(FormSheet fs)
{
}
| |
| | |
|
|
|
Dann werden die zwei FormButtons mit
Leben gefüllt. Deren Betätigung löst jeweils
eine neue Transition aus. Nach dem
Betätigen des "Ok"-Buttons gilt die Auswahl der Videos
als beendet und der Kunde muss seine Videos bezahlen. Nach
Auswahl des "Cancel"-Buttons wird der Ausleihvorgang
abgebrochen und bereits vorgenommene Veränderungen
rückgängig gemacht.
|
|
|
| | |
|
fs.getButton(FormSheet.BTNID_OK).setAction( new sale.Action()
{
public void doAction(SaleProcess p, SalesPoint sp)
{
selectionGate.setNextTransition(toPayingTransition);
}
});
fs.getButton(FormSheet.BTNID_CANCEL).setAction( new sale.Action()
{
public void doAction(SaleProcess p, SalesPoint sp)
{
selectionGate.setNextTransition(
GateChangeTransition.CHANGE_TO_ROLLBACK_GATE);
}
});
| |
| | |
|
|
|
Das besondere am "Cancel"-Button ist, daß er zum Rollback-Gate führt. An diesem werden
die bereits ausgewählten und sich im Datenkorb
befindlichen Videos wieder in den Bestand des Verleihs
einsortiert. Eine zum Rollback-Gate
führende Transition ist bereits
als Konstante CHANGE_TO_ROLLBACK_GATE
in GateChangeTransition gespeichert.
|
|
|
Die Anpassungen des FormSheets sind beendet,
der FormSheetContentCreator ist nun
vollständig.
|
|
|
Am Ende der perform -Methode muss jetzt noch das
gerade erstellte FormSheet am richtigen
Gate gesetzt und das nächste Gate zurückgegeben werden.
|
|
|
| | |
|
selectionGate.setFormSheet(ttfs);
return selectionGate;
| |
| | |
|
|
|
Die zum Auswählen führende Transition ist nun abgeschlossen. Als
nächstes wird die toPayingTransition
benötigt, die nach der Auswahl der Videos zum
rentGate führt. Dort wird errechnet, wieviel
Geld der Kunde in den Münzschacht des Automaten werfen
muss, um sich alle gewünschten Videos ausleihen zu
können. Als erstes wird wieder das Gate selbst initialisiert:
|
|
|
| | |
|
rentGate = new UIGate((FormSheet) null, (MenuSheet) null);
| |
| | |
|
|
|
Wie schon bei der ersten Transition
wird auch hier die Methode perform implementiert,
die das nächste Gate vorbereitet:
|
|
|
| | |
|
toPayingTransition = new Transition()
{
public Gate perform(SaleProcess pOwner, User usr)
{
}
};
| |
| | |
|
|
|
In der perform -Methode muß zunächst
der zu zahlende Betrag ausgerechnet und
toPayValue zugewiesen werden. Dazu wird die
Methode sumBasket benutzt, die im
Interface DataBasket definiert ist. Sie
benötigt als Parameter ein Objekt vom Typ DataBasketCondition , ein Objekt vom Typ BasketEntryValue und einen Initialwert vom
Typ Value , auf den der Inhalt des
Datenkorbes aufsummiert wird.
|
|
|
Das Interface DataBasketCondition
definiert eine Beschreibung eines DataBasketEntry . Sie wird für
Filterzwecke benutzt. In diesem Fall soll eine DataBasketCondition benutzt werden, die alle
im DataBasket vorhandenen StockItems umfaßt. Ein derartiges
Objekt wird mit ALL_STOCK_ITEMS bereits
als Konstante in DataBasketConditionImpl , einer einfachen
Implementierung von DataBasketCondition , zur Verfügung
gestellt.
|
|
|
BasketEntryValue ist ein
Hilfsinterface, das lediglich die Methode getEntryValue enthält. Diese erwartet
als Parameter einen DataBasketEntry und
gibt dessen Wert zurück. Auch für dieses Interface
existieren in BasketEntryValue bereits
Konstanten, die häufige Anwendungsfälle abdecken. In
diesem Fall wird ONLY_STOCK_ITEMS
verwendet, ein BasketEntryValue , der
nur StockItems verarbeiten kann.
|
|
|
Das Interface Value ist bereits
bekannt. In diesem Programm werden Katalogeinträge
verwendet, die QuoteValues zum
Speichern der Werte benutzen. Es muß also ein
Initialwert von diesem Typ angelegt werden. Die Komponenten
von QuoteValues sind IntegerValues .
|
|
|
Um die im Datenkorb enthaltenen StockItems aufzusummieren, wird folgender
Code in die perform -Methode eingefügt:
|
|
|
| | |
|
final DataBasketCondition dbc =
DataBasketConditionImpl.ALL_STOCK_ITEMS;
BasketEntryValue bev = BasketEntryValues.ONLY_STOCK_ITEMS;
QuoteValue qvSum = new QuoteValue(new IntegerValue(0),
new IntegerValue(0));
pOwner.getBasket().sumBasket(dbc, bev, qvSum);
| |
| | |
|
|
|
Die Variable dbc ist deshalb final deklariert,
damit sie später noch einmal innerhalb der inneren Klasse
des noch folgenden FormSheetContentCreator verwendet werden
kann. Nun muss aus der Summe der zu zahlende Betrag extrahiert
werden. Ein QuoteValue besteht aus Einkaufs- und
Verkaufspreis. In qvSum befinden sich somit die
Summen der entsprechenden Werte in den
Datenkorbeinträgen. Die Methode getBid liefert die Summe der
Verkaufspreise. Es ist also einzufügen:
|
|
|
| | |
|
toPayValue = (IntegerValue)qvSum.getBid();
| |
| | |
|
|
|
Damit ist der zu zahlende Betrag ermittelt. Mit dessen Hilfe
kann nun das FormSheet für das
nächste Gate aufgebaut
werden. Dazu wird erneut eine TextInputForm verwendet.
|
|
|
| | |
|
FormSheet tif = new TextInputForm(
"Paying",
"You have to pay" + myCurrency.toString(toPayValue) + ".",
myCurrency.toString(toPayValue));
| |
| | |
|
|
|
Leider besitzt das FormSheet nur einen
"OK"-Button, und auch dieser schließt
zur Zeit lediglich das FormSheet , ohne
weitere Aktionen auszuführen. Es ist also notwendig,
einen FormSheetContentCreator an das
FormSheet zu übergeben, der in
seiner createFormSheetContent -Methode
die nötigen Änderungen vornimmt.
|
|
|
| | |
|
tif.addContentCreator(new FormSheetContentCreator()
{
protected void createFormSheetContent(FormSheet fs)
{
}
});
| |
| | |
|
|
|
Da in den, an die Buttons angehängten, Aktionen auf das
FormSheet zugegriffen werden muß,
jedoch aus anonymen Klassen heraus nur als final
erstellte Variablen des umschließenden Blockes verwendet
werden können, wird in
createFormSheetContent zunächst eine
derartige Variable angelegt und in ihr das als Paramter
übergebene FormSheet zugewiesen:
|
|
|
| | |
|
final TextInputForm tifFinal = (TextInputForm)fs;
| |
| | |
|
|
|
Das FormSheet soll drei FormButtons enthalten. Zuerst einen
"Ok"-Button, dann einen "Back"-Button, zuletzt einen
"Cancel"-Button. Dafür wird zunächst der
"Cancel"-Button entfernt, damit die Buttons später in
gewünschter Reihenfolge stehen.
|
|
|
| | |
|
fs.removeButton(FormSheet.BTNID_CANCEL);
| |
| | |
|
|
|
Nun wird ein neuer "Ok"-Button eingefügt
-- Action wird wie üblich als
anonyme Klasse implementiert:
|
|
|
| | |
|
fs.getButton(FormSheet.BTNID_OK).setAction( new sale.Action()
{
});
| |
| | |
|
|
|
Die nach dem Betätigen des Buttons auszuführende
Aktion besteht darin, sich die Eingabe als bezahlten Betrag zu
merken und die nächste Transition
zu setzen. Bei der Verarbeitung des eingegebenen Textes
könnte eine ParseException (aus dem Paket
java.text ) auftreten, daher werden die
Anweisungen in einem try -Block eingeschlossen und
die Exception mit einem catch
abgefangen. Im try -Block werden die Videos in den
Bestand des Kunden übernommen, d.h. in seinem
Kundenkonto mit ihrem Ausleihdatum vermerkt. In die
Action wird folgende
doAction -Methode eingefügt:
|
|
|
| | |
|
public void doAction(SaleProcess p, SalesPoint sp)
{
try {
paidValue = (IntegerValue)myCurrency.parse(tifFinal.getText());
videoIterator = db.iterator(dbc);
rentGate.setNextTransition(toDecisionTransition);
}
catch (ParseException pexc) {
}
}
| |
| | |
|
|
|
Um nun die ParseException korrekt zu behandeln,
soll eine Meldung ausgegeben werden. Dazu wird eine MsgForm mit dem entsprechenden Text erzeugt
und mit Hilfe der popUpFormSheet -Methode des ProcessContextes angezeigt. Nun erzeugt
jedoch unter Umständen auch popUpFormSheet eine
Exception . Diese wird hier ignoriert. Der
schlimmste Fall, der eintreten kann, ist daß das MsgForm so schnell wieder verschwindet, das
die Meldung nicht gelesen werden konnte. In diesem Fall wird
der Benutzer entweder allein die richtige
Schlußfolgerung ziehen und das Format des von ihm
eingegebenen Textes korrigieren, oder aber er wird nochmal auf
"Ok" drücken und die Fehlermeldung doch
noch erhalten. Es wird in den catch -Block
folgendes eingefügt:
|
|
|
| | |
|
MsgForm mf = new MsgForm("Error",
"The specific amount does not have " +
"an appropriate format.");
try {
p.getContext().popUpFormSheet(p, mf);
}
catch(InterruptedException iexc) {
}
| |
| | |
|
|
|
Damit ist ein neuer "Ok"-Button mit
angemessener Aktion eingefügt. Es folgt ein
"Back"-Button, der einfach eine neue GateChangeTransition als nächste Transition setzt. Diese führt ohne
weitere Aktion zurück zur Auswahl:
|
|
|
| | |
|
fs.addButton("Back", 101, new sale.Action()
{
public void doAction(SaleProcess s, SalesPoint sp)
{
rentGate.setNextTransition(
new GateChangeTransition(selectionGate));
}
});
| |
| | |
|
|
|
Ähnlich einfach gestaltet sich der
"Cancel"-Button. Der einzige Unterschied ist,
daß er natürlich nicht zur Auswahl
zurückführt, sondern zum Rollback-Gate .
|
|
|
| | |
|
fs.addButton("Cancel", 102, new sale.Action()
{
public void doAction(SaleProcess s, SalesPoint sp)
{
rentGate.setNextTransition(
GateChangeTransition.CHANGE_TO_ROLLBACK_GATE);
}
});
| |
| | |
|
|
|
Die Anpassung des FormSheets ist nun
beendet, der FormSheetContentCreator
vollständig.
|
|
|
Am Ende der perform -Methode muß das gerade
erstellte FormSheet am richtigen
Gate gesetzt und das nächste
Gate zurückgegeben werden.
|
|
|
| | |
|
rentGate.setFormSheet(tif);
return rentGate;
| |
| | |
|
|
|
Der Kunde konnte nun am rentGate seine Videos
bezahlen. Die setupMachine -Methode wird um eine
weitere Transition ergänzt. Diese
führt vom rentGate zum
decisionGate . An diesem wird entschieden, ob der
Kunde zuwenig, zuviel oder genau die geforderte Summe in den
Münzschacht geworfen hat. Dementsprechend werden
unterschiedliche Aktionen ausgeführt.
|
|
|
Die toDecisionTransition wird wie folgt erstellt:
|
|
|
| | |
|
toDecisionTransition = new Transition()
{
public Gate perform(SaleProcess pOwner, User usr)
{
}
};
| |
| | |
|
|
|
Die Überprüfung des Geldes erfolgt mit Hilfe von
if -Anweisungen innerhalb der
perform -Methode. Außerdem wird der zu
zahlende Betrag zum Geldbestand des Videoautomaten
hinzugefügt, wenn ausreichend Geld bezahlt wurde:
|
|
|
| | |
|
if (paidValue.compareTo(toPayValue) >= 0) {
if (toPayValue.getValue().intValue() > 0)
((CountingStock)Shop.getTheShop().getStock(
"coin slot")).add(EUROCurrencyImpl.CENT_STCK_1,
toPayValue.getValue().intValue(),
pOwner.getBasket());
if (paidValue.compareTo(toPayValue) == 0)
payAssessment = 0;
else
payAssessment = 1;
}
else
payAssessment = -1;
| |
| | |
|
|
|
Wurde genau der gewünschte Betrag eingeworfen, wird
payAssessment auf 0 gesetzt, wurde zu viel
bezahlt auf 1 und wurde zu wenig bezahlt auf -1.
|
|
|
Am Ende der perform -Methode muß noch das
decisionGate als nächstes zu betretendes
Gate gesetzt werden:
|
|
|
|
|
|
Das decisionGate wird im folgenden implementiert:
|
|
|
| | |
|
decisionGate = new Gate()
{
};
| |
| | |
|
|
|
Im decisionGate steht die
getNextTransition -Methode, die entscheidet,
welche Transition bei welchem Ereignis
ausgelöst wird.
|
|
|
| | |
|
public Transition getNextTransition(SaleProcess pOwner, User usr)
throws InterruptedException
{
}
| |
| | |
|
|
|
Sollte der Kunde zu wenig Geld in den Münzschacht des
Automaten eingeworfen haben, wird er erneut zum Bezahlen
aufgefordert. Wurde genau der zu zahlende Betrag gegeben, wird
das Ausleihen beendet und das Commit-Gate erreicht. Erwartet der Kunde
Wechselgeld, so führt die
toGetChangeTransition zum
getChangeGate . Sollte der eigentlich
unmögliche Fall eintreten, daß die Variable
payAssessment mit keinem der drei Werte belegt
ist, tritt das default -Ereignis ein. Es werden
alle laufenden Prozesse beendet und die Anwendung springt zum
Quit-Gate .
|
|
|
Die Auswahl der jeweils auszuführenden Aktion erfolgt mit
Hilfe einer switch -Anweisung in der
getNextTransition -Methode:
|
|
|
| | |
|
switch(payAssessment) {
case -1:
FormSheet mf = new MsgForm("Error",
"You have to pay more!",
false);
pOwner.getContext().popUpFormSheet(pOwner, mf);
return new GateChangeTransition(rentGate);
case 0:
return GateChangeTransition.CHANGE_TO_COMMIT_GATE;
case 1:
return toGetChangeTransition;
default:
FormSheet mf2 = new MsgForm("Error",
"Internal error at Decision Gate. "
+ "Will quit process.",
false);
pOwner.getContext().popUpFormSheet(pOwner, mf2);
return GateChangeTransition.CHANGE_TO_QUIT_GATE;
}
| |
| | |
|
|
|
Als nächstes muss die bereits erwähnte Methode
commitTransfer in der anonymen Klasse des Gates implementiert werden. Zunächst
wird das aktuelle Datum vom Shop
geholt, mit dem der Konstruktor von CassetteStoringStockItem bedient werden
muss. Dann wird der videoIterator durchlaufen und
alle geliehenen Videos auf den Bestand des Kunden gelegt. Die
aufrufende Methode setzt direkt oder nach der
Wechselgeldübergabe das Commit-Gate , was dafür sorgt, dass
anschliessend die im DataBasket
liegenden Videos aus dem Bestand endgültig gestrichen
werden.
|
|
|
| | |
|
public void commitTransfer()
{
while (videoIterator.hasNext()) {
Object date = Shop.getTheShop().getTimer().getTime();
CountingStockItemDBEntry cassetteItem =
(CountingStockItemDBEntry) videoIterator.next();
int number = cassetteItem.count();
for (; number > 0; number --) {
customer.addVideoCassette(
new CassetteStoringStockItem(cassetteItem.getSecondaryKey(),
date));
}
}
}
| |
| | |
|
|
|
Nun initialisieren wir das GetChangeGate , anschliessend die Transition , die zu dem Gate führt.
|
|
|
| | |
|
getChangeGate = new UIGate(null, null);
| |
| | |
|
|
|
Als letztes wird die noch fehlende toGetChangeTransition implementiert. Zuerst
wird wieder eine Transition angelegt
und in ihr die perform -Methode implementiert:
|
|
|
| | |
|
toGetChangeTransition = new Transition()
{
public Gate perform(SaleProcess pOwner, User usr)
{
}
};
| |
| | |
|
|
|
In der dieser Methode wird die Oberfläche vorbereitet,
die beim Erreichen des neuen Gates
dargestellt werden soll. Es wird ein einfaches MsgForm erzeugt, das den zurückgegebenen
Betrag anzeigt.
|
|
|
| | |
|
MsgForm mf = new MsgForm(
"GetChange",
"You get "
+ myCurrency.toString(
(IntegerValue)paidValue.subtract(toPayValue))
+ " Change.");
| |
| | |
|
|
|
Auch hier besitzt das FormSheet nur
einen "OK"-Button, der lediglich das Fenster
schließt. Es ist also wieder notwendig einen FormSheetContentCreator an das FormSheet zu übergeben, der in seiner
createFormSheetContent -Methode dem
Button eine neue Aktion zuordnet. Mit der getButton -Methode wird der Button aus dem
FormSheet geholt, und die setAction -Methode ändert die Aktion, die
vom Buttons ausgelöst wird. In unserer Anwendung wird
zum Commit-Gate gesprungen, da
der Ausleihvorgang mit Rückgabe des Wechselgeldes
abgeschlossen ist.
|
|
|
| | |
|
mf.addContentCreator(new FormSheetContentCreator()
{
protected void createFormSheetContent(FormSheet fs)
{
fs.getButton(FormSheet.BTNID_OK).setAction(new Action()
{
public void doAction (SaleProcess s, SalesPoint sp)
{
getChangeGate.setNextTransition(
GateChangeTransition.CHANGE_TO_COMMIT_GATE);
}
});
}
});
| |
| | |
|
|
|
Die perform -Methode ist fertig
implementiert. Nach ihr wird das betreffende FormSheet am zu betretenden
getChangeGate gesetzt, und das Gate , daß als nächstes zu betreten
ist, wird zurückgegeben:
|
|
|
| | |
|
getChangeGate.setFormSheet(mf);
return getChangeGate;
| |
| | |
|
|
|
Alle wichtigen Transistions sind
fertiggestellt.
|
|
|
Die setupMachine -Methode ist damit
abgeschlossen. Als letztes muß die Methode
getInitialGate implementiert werden, die das
Start-Gate des RentProcess
liefert und ihn in Gang setzt:
|
|
|
| | |
|
public Gate getInitialGate()
{
setupMachine();
return capabilityGate;
}
| |
| | |
|
|
|
DefaultCounterFormCreator.java |
|
|
Um den Prozeß in die Anwendung einzubinden wird in der
DefaultCounterFormCreator -Klasse die
doAction -Methode des
"rent"-Buttons mit einer Zeile Code
ausgestattet:
|
|
|
| | |
|
s.runProcess(new RentProcess());
| |
| | |
|
|
|
Viel Spaß beim Ausleihen der Videos!!
|
|
|
|
Hier der Quelltext der in diesem Kapitel geänderten Klassen:
|
|
|
|
|