Der Ausleihvorgang
  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 DataBasketsp apilogo, Stocksp apilogo und Catalogsp apilogo sein.  
  Prozesse werden im Framework von der Klasse SaleProcesssp apilogo 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 Gatesp apilogo, die Zustandsübergänge durch Objekte vom Typ Transitionsp apilogo realisiert.  
  Eine Transitionsp apilogo wird als eine unteilbare (atomare) Operation abgearbeitet, sie wird vom Framework nicht unterbrochen. Daher darf es bei der Abarbeitung einer Transitionsp apilogo auf keinen Fall zu einer Kommunikation mit dem Benutzer oder irgendeiner anderen langwierigen, möglicherweise endlosen Aktion kommen.  
  Im Gegensatz zu Transitionssp apilogo dürfen Gatessp apilogo unterbrochen werden (signalisiert durch eine InterruptedException). Für die Nutzerinteraktion gibt es eine spezielle Art von Gatessp apilogo, die UIGatessp apilogo. Es ist vorgesehen, an ihm ein FormSheetsp apilogo oder ein MenuSheetsp apilogo (oder beides) zu setzen. Die Entscheidung, welche der von einem UIGatesp apilogo wegführenden Transitionensp apilogo 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 Gatessp apilogo 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 Gatessp apilogo werden benötigt, um zu entscheiden, ob genug Geld bezahlt wurde und ggf. Wechselgeld zu geben.  
  Außerdem werden vier nichttriviale Transitionssp apilogo benötigt. "Nichttrivial" heißt, es kann nicht einfach von einem Gatesp apilogo zum nächsten gewechselt werden, sondern es ist zusätzlich das nächste Gatesp apilogo vorzubereiten.  
  Die Zustände und Zustandsübergänge des Automaten werden in der folgenden Abbildung noch einmal verdeutlicht:
Der Ausleihvorgang als deterministischer Automat
Abbildung 2.1: Der Ausleihvorgang als deterministischer Automat
 
  Zunächst jedoch muß der neue SaleProcesssp apilogo 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.  
   neue Javaklasse    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 Gatessp apilogo und Transitionssp apilogo 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 SaleProcesssp apilogo 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 Shopsp apilogo und von diesem der entsprechende Catalogsp apilogo geholt werden.  
 

  myCurrency = (Currency)Shop.getTheShop().getCatalog("EURO");
        
 
  Der derzeit gültige DataBasketsp apilogo 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 UIGatessp apilogo in der Methode setupMachine angelegt werden. Zunächst das, an dem sich die Kunden anmelden.  
  Das Capability-Gatesp apilogo stellt den Startzustand des Automaten dar. Es wird keine Transitionsp apilogo geben, die zu ihm hin führt, deshalb wird das FormSheetsp apilogo mir Hilfe eines FormSheetContentCreatorssp apilogo erstellt und direkt übergeben. Alles, was zur Abarbeitung des Gatessp apilogo 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 Gatessp apilogo. In diesem Fall wird ein FormSheetsp apilogo zur Eingabe der Kundennummer gesetzt. Dazu wird ein TextInputFormsp apilogo verwendet. Dies ist ein FormSheetsp apilogo, das ein Label und eine Eingabezeile vom Typ JTextInput enthält.  
 

  final TextInputForm tif =
    new TextInputForm("Customer-ID", "Customer-ID", "");
        
 
  Zur Ausgestaltung des FormSheetssp apilogo wird der bereits bekannte FormSheetContentCreatorsp apilogo erstellt, um bei einem möglichen Abspeichern und erneutem Laden das FormSheetsp apilogo wiederherstellen zu können:  
 

  tif.addContentCreator(new FormSheetContentCreator() 
  {
    protected void createFormSheetContent(FormSheet fs)
    {
    }
  });
        
 
  Nun wird der Standard "Ok"-Button mit einer Actionsp apilogo 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 Actionsp apilogo 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 Actionsp apilogo für den "Cancel"-Button, der zum Rollback-Gatesp apilogo 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 FormSheetContentCreatorsp apilogo ist abgeschlossen und das Gatesp apilogo kann jetzt mit dem FormSheetsp apilogo initialisiert werden. Da kein MenuSheetsp apilogo benötigt wird, wird dieses null gesetzt.  
 

  capabilityGate = new UIGate((FormSheet) tif, (MenuSheet) null);
        
 
  Die weiteren Gatessp apilogo müssen entsprechend durch den Verlauf beeinflusst werden. Dies wird verwirklicht, indem man statt der Gatessp apilogo die entsprechenden Transitionssp apilogo benutzt, die zu den Gatessp apilogo führen. Die Transitionsp apilogo bereitet das Gatesp apilogo dann während der Laufzeit vor. Zunächst initialisieren wir das Gatesp apilogo, wobei das FormSheetsp apilogo hier ebenfalls null gesetzt wird. Dieses wird erst während der Laufzeit durch die Transitionsp apilogo 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 Transitionsp apilogo als anonyme Klasse realisiert. Dies wird auch mit allen weiteren Transitionssp apilogo geschehen. In der anonymen Klasse ist die performsp apilogo-Methode zu implementieren:  
 

  public Gate perform(SaleProcess pOwner, User usr)
  {
  } 
        
 
  Diese Methode ist in einer Transitionsp apilogo dafür verantwortlich, alle nötigen Arbeiten auszuführen, die bis zum Erreichen des nächsten Gatessp apilogo nötig sind. Dazu gehört auch das Vorbereiten des nächsten Gatessp apilogo selbst. In diesem Fall führt die Transitionsp apilogo von der erfolgreichen Eingabe der Kundennummer (capabilityGate) zu dem Gatesp apilogo, 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 FormSheetsp apilogo 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 TwoTableFormSheetsp apilogo eine Reihe von createsp apilogo-Methoden für viele Kombinationen von Parametern. An dieser Stelle wurde die Methode mit folgenden Parametern verwendet:
  • Titel des FormSheetssp apilogo
  • Quelle, hier der CountingStocksp apilogo
  • Ziel, hier der DataBasketsp apilogo
  • UIGatesp apilogo, an dem das FormSheetsp apilogo 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
  • TableEntryDescriptorsp apilogo der Quelltabelle
  • TableEntryDescriptorsp apilogo der Zieltabelle
  • Strategie für die Bewegung der Einträge
 
  Als nächstes muß das FormSheetsp apilogo den Bedürfnissen des selectionGate angepaßt werden. Dazu wird hinter der Erzeugung des FormSheetssp apilogo ein FormSheetContentCreatorsp apilogo 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 FormSheetssp apilogo aufgerufen, um die notwendigen Anpassungen vorzunehmen.  
 

  protected void createFormSheetContent(FormSheet fs)
  {
  }
        
 
  Dann werden die zwei FormButtonssp apilogo mit Leben gefüllt. Deren Betätigung löst jeweils eine neue Transitionsp apilogo 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-Gatesp apilogo 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-Gatesp apilogo führende Transitionsp apilogo ist bereits als Konstante CHANGE_TO_ROLLBACK_GATEsp apilogo in GateChangeTransitionsp apilogo gespeichert.  
  Die Anpassungen des FormSheetssp apilogo sind beendet, der FormSheetContentCreatorsp apilogo ist nun vollständig.  
  Am Ende der perform-Methode muss jetzt noch das gerade erstellte FormSheetsp apilogo am richtigen Gatesp apilogo gesetzt und das nächste Gatesp apilogo zurückgegeben werden.  
 

  selectionGate.setFormSheet(ttfs);
  return selectionGate;
        
 
  Die zum Auswählen führende Transitionsp apilogo 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 Gatesp apilogo selbst initialisiert:  
 

  rentGate = new UIGate((FormSheet) null, (MenuSheet) null);
        
 
  Wie schon bei der ersten Transitionsp apilogo wird auch hier die Methode perform implementiert, die das nächste Gatesp apilogo 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 sumBasketsp apilogo benutzt, die im Interface DataBasketsp apilogo definiert ist. Sie benötigt als Parameter ein Objekt vom Typ DataBasketConditionsp apilogo, ein Objekt vom Typ BasketEntryValuesp apilogo und einen Initialwert vom Typ Valuesp apilogo, auf den der Inhalt des Datenkorbes aufsummiert wird.  
  Das Interface DataBasketConditionsp apilogo definiert eine Beschreibung eines DataBasketEntrysp apilogo. Sie wird für Filterzwecke benutzt. In diesem Fall soll eine DataBasketConditionsp apilogo benutzt werden, die alle im DataBasketsp apilogo vorhandenen StockItemssp apilogo umfaßt. Ein derartiges Objekt wird mit ALL_STOCK_ITEMSsp apilogo bereits als Konstante in DataBasketConditionImplsp apilogo, einer einfachen Implementierung von DataBasketConditionsp apilogo, zur Verfügung gestellt.  
  BasketEntryValuesp apilogo ist ein Hilfsinterface, das lediglich die Methode getEntryValuesp apilogo enthält. Diese erwartet als Parameter einen DataBasketEntrysp apilogo und gibt dessen Wert zurück. Auch für dieses Interface existieren in BasketEntryValuesp apilogo bereits Konstanten, die häufige Anwendungsfälle abdecken. In diesem Fall wird ONLY_STOCK_ITEMSsp apilogo verwendet, ein BasketEntryValuesp apilogo, der nur StockItemssp apilogo verarbeiten kann.  
  Das Interface Valuesp apilogo ist bereits bekannt. In diesem Programm werden Katalogeinträge verwendet, die QuoteValuessp apilogo zum Speichern der Werte benutzen. Es muß also ein Initialwert von diesem Typ angelegt werden. Die Komponenten von QuoteValuessp apilogo sind IntegerValuessp apilogo.  
  Um die im Datenkorb enthaltenen StockItemssp apilogo 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 FormSheetContentCreatorsp apilogo 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 getBidsp apilogo 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 FormSheetsp apilogo für das nächste Gatesp apilogo aufgebaut werden. Dazu wird erneut eine TextInputFormsp apilogo verwendet.  
 

  FormSheet tif = new TextInputForm(
    "Paying",
    "You have to pay" + myCurrency.toString(toPayValue) + ".", 
    myCurrency.toString(toPayValue));
        
 
  Leider besitzt das FormSheetsp apilogo nur einen "OK"-Button, und auch dieser schließt zur Zeit lediglich das FormSheetsp apilogo, ohne weitere Aktionen auszuführen. Es ist also notwendig, einen FormSheetContentCreatorsp apilogo an das FormSheetsp apilogo zu übergeben, der in seiner createFormSheetContentsp apilogo-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 FormSheetsp apilogo 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 FormSheetsp apilogo zugewiesen:  
 

  final TextInputForm tifFinal = (TextInputForm)fs;
        
 
  Das FormSheetsp apilogo soll drei FormButtonssp apilogo 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 -- Actionsp apilogo 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 Transitionsp apilogo 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 MsgFormsp apilogo mit dem entsprechenden Text erzeugt und mit Hilfe der popUpFormSheetsp apilogo-Methode des ProcessContextessp apilogo angezeigt. Nun erzeugt jedoch unter Umständen auch popUpFormSheetsp apilogo eine Exception. Diese wird hier ignoriert. Der schlimmste Fall, der eintreten kann, ist daß das MsgFormsp apilogo 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 GateChangeTransitionsp apilogo als nächste Transitionsp apilogo 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-Gatesp apilogo.  
 

  fs.addButton("Cancel", 102, new sale.Action()
  {
    public void doAction(SaleProcess s, SalesPoint sp)
    {
      rentGate.setNextTransition(
        GateChangeTransition.CHANGE_TO_ROLLBACK_GATE);
    }
  });   
       
 
  Die Anpassung des FormSheetssp apilogo ist nun beendet, der FormSheetContentCreator vollständig.  
  Am Ende der perform-Methode muß das gerade erstellte FormSheetsp apilogo am richtigen Gatesp apilogo gesetzt und das nächste Gatesp apilogo zurückgegeben werden.  
 

  rentGate.setFormSheet(tif);
  return rentGate;
        
 
  Der Kunde konnte nun am rentGate seine Videos bezahlen. Die setupMachine-Methode wird um eine weitere Transitionsp apilogo 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 Gatesp apilogo gesetzt werden:  
 

  return decisionGate;
        
 
  Das decisionGate wird im folgenden implementiert:  
 

  decisionGate = new Gate()
  {
  };
        
 
  Im decisionGate steht die getNextTransition-Methode, die entscheidet, welche Transitionsp apilogo 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-Gatesp apilogo 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-Gatesp apilogo.  
  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 Gatessp apilogo implementiert werden. Zunächst wird das aktuelle Datum vom Shopsp apilogo geholt, mit dem der Konstruktor von CassetteStoringStockItemsp apilogo 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-Gatesp apilogo, was dafür sorgt, dass anschliessend die im DataBasketsp apilogo 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 GetChangeGatesp apilogo, anschliessend die Transitionsp apilogo, die zu dem Gatesp apilogo führt.  
 

  getChangeGate = new UIGate(null, null);
        
 
  Als letztes wird die noch fehlende toGetChangeTransitionsp apilogo implementiert. Zuerst wird wieder eine Transitionsp apilogo 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 Gatessp apilogo dargestellt werden soll. Es wird ein einfaches MsgFormsp apilogo 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 FormSheetsp apilogo nur einen "OK"-Button, der lediglich das Fenster schließt. Es ist also wieder notwendig einen FormSheetContentCreatorsp apilogo an das FormSheetsp apilogo zu übergeben, der in seiner createFormSheetContentsp apilogo-Methode dem Button eine neue Aktion zuordnet. Mit der getButtonsp apilogo-Methode wird der Button aus dem FormSheetsp apilogo geholt, und die setActionsp apilogo-Methode ändert die Aktion, die vom Buttons ausgelöst wird. In unserer Anwendung wird zum Commit-Gatesp apilogo 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 FormSheetsp apilogo am zu betretenden getChangeGate gesetzt, und das Gatesp apilogo, daß als nächstes zu betreten ist, wird zurückgegeben:  
 

  getChangeGate.setFormSheet(mf);
  return getChangeGate;
        
 
  Alle wichtigen Transistionssp apilogo sind fertiggestellt.  
  Die setupMachine-Methode ist damit abgeschlossen. Als letztes muß die Methode getInitialGate implementiert werden, die das Start-Gatesp apilogo des RentProcess liefert und ihn in Gang setzt:  
 

  public Gate getInitialGate()
  {
    setupMachine();
    return capabilityGate;
  }      
        
 
   neue Javaklasse    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!!  
 Quelltexte
  Hier der Quelltext der in diesem Kapitel geänderten Klassen:  
 
vorherige Seite  Der Kunde Die Rückgabe des Videos  naechste Seite
 

by kk15

Valid HTML 4.01!