|
|
|
|
|
Nachdem Kataloge und Bestände vorhanden sind, soll dem
Kunden das Angebot auch angezeigt werden.
|
|
|
Es muß nun ein FormSheet erzeugt und
zurückgegeben werden, das die Tabelle mit dem Angebot
enthält.
Außerdem sollten die FormSheets
Buttons enthalten, nämlich zum Verleihen und Zurückgeben von
Videos.
|
FormSheet
|
|
Wenn man sich die Klasse FormSheet
ansieht, wird man feststellen, daß sie zwei Arten von
Konstruktoren hat: Zum einen ist es möglich, einem FormSheet eine Komponente zu übergeben,
zum anderem kann man ihm aber auch einen FormSheetContentCreator übergeben. Ein
FormSheetContentCreator kann vom FormSheet benutzt werden, um seinen Inhalt zu
erzeugen. Die Einführung des ContentCreators in das Framework hat folgende
Bewandnis: Es ist nicht immer möglich, Swing-Komponenten
korrekt abzuspeichern (zu serialisieren). Daher kann auch ein FormSheet , das mit Hilfe einer Komponente
erzeugt wurde, nicht vollständig abgespeichert
werden. Ergebnis ist, daß ein solches FormSheet zwar gespeichert und auch wieder
geladen wird, jedoch nach dem Laden keinen Inhalt mehr
besitzt. Die Swing -Komponente konnte nicht
mitgesichert und so auch nicht wiederhergestellt werden.
Lösung ist die Einführung eines FormSheetContentCreators . Dieser besitzt eine
Methode createFormSheetContent , die
jedesmal aufgerufen wird, wenn der Inhalt des FormSheets neu erzeugt werden muß --
z.B. nach dem Laden. Es sollte also jedes FormSheet , das konkret wiederhergestellt
werden soll, mit einem FormSheetContentCreator ausgestattet werden.
|
FormSheetContentCreator
|
|
Ein weiterer Vorteil dieses Konzepts ist, daß einem
FormSheet mit addContentCreator weitere Objekte vom Typ
FormSheetContentCreator übergeben
werden können. Diese werden beim Aufbauen des FormSheets nacheinander aufgerufen und
können jeder für sich Veränderungen vornehmen,
so daß an einem vorgefertigten FormSheet weitere Anpassungen durch das
Hinzufügen eines ContentCreators
möglich sind.
|
|
|
Es soll im folgenden ein FormSheetContentCreator erstellt werden, der
in seiner createFormSheetContent -Methode die
gewünschte Tabelle und die benötigten Buttons
erzeugt. Mit dessen Hilfe wird dann in der getDefaultFormSheet -Methode ein FormSheet erzeugt und zurückgegeben. Es
wird zunächst die Klasse
DefaultCounterFormCreator wie folgt mit Code
gefüllt:
|
|
|
 |  |  |
 |
import sale.*;
public class DefaultCounterFormCreator extends FormSheetContentCreator
{
public DefaultCounterFormCreator()
{
super();
}
}
|  |
 |  |  |
|
|
|
Es wird, wie oben schon erwähnt, die Methode
createFormSheetContent in diese Klasse
eingefügt. Sie enthält als Parameter das FormSheet, mit dem
es arbeiten soll:
|
FormSheetContent
|
|
 |  |  |
 |
public void createFormSheetContent(FormSheet map)
{
}
|  |
 |  |  |
|
|
|
Zunächst soll die Tabelle mit den Videos in das
FormSheet eingefügt werden. Dazu muß
der Videobestand aus der globalen Liste der Bestände
herausgesucht werden:
|
|
|
 |  |  |
 |
CountingStockImpl cs =
(CountingStockImpl)Shop.getTheShop().getStock("Video-Countingstock");
|  |
 |  |  |
|
|
|
Es wird mit der statischen Methode getTheShop
der derzeit aktive Shop ermittelt und danach
von ihm der benötigte Stock geholt.
|
|
|
Um dem Compiler die Klasse CountingStockImpl
bekannt zu machen, ist eine weitere import -Anweisung
zu der schon vorhandenen hinzuzufügen:
|
|
|
|
|
|
Um die Tabelle zu erstellen, kann eine der statischen Methoden
von SingleTableFormSheet benutzt
werden. Diese Klasse aus dem Paket data.stdforms ist ideal zum Erstellen von
FormSheets , die eine Tabelle mit dem
Inhalt eines Stocks , Catalogs oder DataBaskets enthalten sollen. Alles was zu
tun ist, ist die passende create -Methode aufzurufen.
|
SingleTableFormSheet
|
|
Ganz so einfach ist es dann aber leider doch nicht. Es gibt
zwar eine create -Methode, die lediglich
einen Namen, einen CountingStock und
ein UIGate , das null sein
kann, erwartet (UIGates werden
später erklärt). Diese ist aber nicht verwendbar, da
sie die Werte vom Typ QuoteValue , die
sie von den darzustellenden VideoCassettes
über getValue bekommt, nicht
verarbeiten kann. Es bleibt also nichts anderes übrig,
als die create -Methode zu verwenden,
die zusätzlich einen TableEntryDescriptor (kurz TED), also eine
Beschreibung des Tabelleneintrags erwartet und ihren eigenen,
noch zu erstellenden TableEntryDescriptor zu
übergeben.
|
TableEntryDescriptor
|
|
Auf den ersten Blick sollte man meinen, ein derartiger Descriptor ließe sich am besten von
DefaultCountingStockItemTED
ableiten. Auf den zweiten Blick hat aber die Ableitung von
einer Unterklasse von DefaultCountingStockItemTED einen bedeutenden
Vorteil: DefaultMoneyBagItemTED macht im
wesentlichen das gleiche wie seine Oberklasse, es stellt aber
den Wert des Eintrags als Währungsangabe dar, nicht
einfach als normale Zahl. Diese Klasse ist zwar
ursprünglich für Einträge eines MoneyBags gedacht, daher auch der Name, ist
aber auch für alle anderen CountingStocks geeignet, deren Wert einen
Geldbetrag bezeichnet.
|
|
|
Es wird die Klasse OfferTED wie folgt erstellt:
|
|
|
 |  |  |
 |
import data.swing.*;
public class OfferTED extends DefaultMoneyBagItemTED
{
}
|  |
 |  |  |
|
|
|
Die Tabelle soll zunächst nur zwei Spalten enthalten: zum
einen den Namen der Videos und zum anderen deren
Preis. Später jedoch, wenn es darum geht, Videos zu
verleihen, soll der Kunde auch erfahren, wieviele Videos
überhaupt verfügbar sind. Es wird also dem
Konstruktor von OfferTED ein Boolescher Wert
übergeben, der angibt, ob die dritte Spalte, die die
Anzahl der verfügbaren Videos enthält, angezeigt
werden soll oder nicht.
|
|
|
Dazu wird in der Klasse zunächst folgende Variable deklariert:
|
|
|
 |  |  |
 |
private boolean withCount;
|  |
 |  |  |
|
|
|
Nun folgt der Konstruktor. Dieser ruft zunächst den
Konstruktor der Oberklasse mit der zu verwendenden Währung
auf. Die Währung wird auf schon bekannte Weise vom aktuellen
Shop aus der globalen Katalogliste geholt.
Außerdem wird die gerade deklarierte Variable initialisiert:
|
|
|
 |  |  |
 |
public OfferTED(boolean withCount)
{
super((Currency)Shop.getTheShop().getCatalog("DM"));
this.withCount = withCount;
}
|  |
 |  |  |
|
|
|
Um dem Compiler die im Konstruktor verwendeten Klassen
Currency und Shop
bekannt zu machen, sind zwei weitere import -Anweisungen
notwendig:
|
|
|
 |  |  |
 |
import data.*;
import sale.*;
|  |
 |  |  |
|
|
|
Als nächstes muß die Methode getColumnCount
implementiert werden. Sie gibt an, aus wieviel Spalten die Tabelle
bestehen soll. Der Rückgabewert ist abhängig
von withCount :
|
Spaltenanzahl
|
|
 |  |  |
 |
public int getColumnCount()
{
return withCount?3:2;
}
|  |
 |  |  |
|
|
|
Um den Spaltentitel zu verändern, wird die Methode
getColumnName angepaßt:
|
Spaltentitel
|
|
 |  |  |
 |
public String getColumnName(int nIndex)
{
return (new String[]{ "Name",
"Price",
"Available" }) [nIndex];
}
|  |
 |  |  |
|
|
|
Die wichtigste Methode, die angepaßt werden muß,
ist jedoch getValueAt . Wie angesprochen
wurde, ist es DefaultCountingStockItemTED und damit auch
DefaultMoneyBagItemTED nicht
möglich, QuoteValues als Werte zu
verarbeiten. Daher muß in getValueAt der
Wert der VideoCassettes aufgesplittet und nur der
Verkaufspreis zurückgegeben werden. Alle anderen Felder
der VideoCassette können ganz normal
ausgewertet und daher die betreffenden Anfragen an die
Oberklasse weitergeleitet werden.
|
|
|
Um die VideoCassette aufsplitten zu können,
muß sie erst einmal ermittelt werden. Dazu ist es
nötig, die Klasse herauszubekommen, von der der
übergebene Record ist. Diese entspricht der
Klasse des Rückgabewertes der getRecord -Methode des verwendeten TableModels . Aus der Dokumentation zu MoneyBagItemTED erfährt man, daß
es mit CountingStockTableModel
zusammenarbeitet. Dessen getRecord -Methode wiederum liefert
Records vom Typ CountingStockTableModel.Record an die
Tabelle.
|
TableModel
|
|
Die getValueAt -Methode sieht also folgendermaßen
aus:
|
|
|
 |  |  |
 |
public Object getValueAt(Object oRecord, int nIndex)
{
if (nIndex == 1) {
VideoCassette vidCassette =
(VideoCassette)
((CountingStockTableModel.Record)oRecord).getDescriptor();
return ((QuoteValue)vidCassette.getValue()).getBid();
}
else return super.getValueAt(oRecord, nIndex);
}
|  |
 |  |  |
|
|
|
Damit ist die Erstellung des TableEntryDescriptors
abgeschlossen.
|
|
|
Es kann nun in DefaultCounterFormCreator unter
der schon bestehenden Anweisung weitergearbeitet werden. Dort
wird, wie angesprochen, über eine der create -Methoden aus SingleTableFormSheet ein FormSheet erzeugt, das die gewünschte
Tabelle enthält. Die
createFormSheetContent -Methode wird um folgende
Zeilen erweitert:
|
FormSheet erzeugen
|
|
 |  |  |
 |
FormSheet fs =
SingleTableFormSheet.create("Available Videocassettes", cs,
null, new OfferTED(false));
|  |
 |  |  |
|
|
|
Die Parameter bedeuten dabei:
-
Titel des
FormSheets
-
darzustellender
CountingStock
-
UIGate : hier null
(zur Bedeutung später)
-
der zu verwendende
TableEntryDescriptor (TED)
|
|
|
Außerdem muß eine import -Anweisung
ergänzt werden, denn SingleTableFormSheet ist aus dem Paket data.stdforms :
|
|
|
|
|
|
Auf diese Weise hat man ein FormSheet
mit der Tabelle der Videos erhalten. Leider soll diese Tabelle
jedoch nicht in irgendeinem FormSheet
enthalten sein, sondern in dem, das
createFormSheetContent als Parameter
übergeben wurde. Daher wird die in dem neuen FormSheet befindliche Komponente ermittelt
und dem übergebenen FormSheet
zugewiesen. Also wird die Methode
createFormSheetContent wie folgt erweitert:
|
|
|
 |  |  |
 |
map.setComponent(fs.getComponent());
|  |
 |  |  |
|
|
|
Die Tabelle befindet sich nun an der gewünschten
Stelle. Es sind jetzt noch Buttons einzufügen. Dazu
werden zunächst alle eventuell schon im FormSheet vorhandenen Buttons entfernt:
|
|
|
|
|
|
Die beiden benötigten Buttons "rent"
und "give back" werden ergänzt:
|
|
|
 |  |  |
 |
map.addButton("rent", 1,
new sale.Action()
{
public void doAction(SaleProcess p, SalesPoint s)
{
//Code zum Ausführen des Verleihvorgangs
}
}
);
map.addButton("give back", 2,
new sale.Action()
{
public void doAction(SaleProcess p, SalesPoint s)
{
//Code zum Ausführen des Rückgabevorgangs
}
}
);
|  |
 |  |  |
|
|
|
Die an addButton übergebenden
Parameter sind die Aufschrift des Buttons, eine Nummer zur
Identifikation und die auszulösende Aktion. Sind schon
Buttons im FormSheet vorhanden,
muß darauf geachtet werden, daß schon vergebene
Nummern nicht noch einmal verwendet werden.
|
|
|
Die auszulösende Aktion implementiert das Action -Interface aus dem Paket sale . Dieses Paket verlangt eine Methode
doAction . In ihr steht der Code, aus
dem die Aktion besteht, die beim Betätigen des Buttons
ausgeführt werden soll. In diesem Fall bleiben die
Methoden noch leer und werden in einem späteren Abschnitt
implementiert.
|
|
|
Der Aufbau der FormSheets ist nun
abgeschlossen. Damit die Kunden das Angebot betrachten
können, muß der eigentliche "Verkaufsstand"
implementiert werden, an dem die Videos ausgeliehen und
zurückgegeben werden können - der
Counter .
|
Kasse
|
|
 |  |  |
 |
import sale.*;
public class Counter extends SalesPoint
{
public Counter(String name)
{
super(name);
}
}
|  |
 |  |  |
|
|
|
Hier wird im Konstruktor der Konstruktor von SalesPoint aufgerufen und der Name des
Counters übergeben.
|
|
|
Außerdem ist eine Methode notwendig, die das
Standard-FormSheet für den
Counter zurückgibt. Die vorhin erstellte Klasse
DefaultCounterFormCreator wird zum Erstellen der
Oberfläche verwendet:
|
|
|
 |  |  |
 |
public FormSheet getDefaultFormSheet()
{
return new FormSheet("Menu",
new DefaultCounterFormCreator(),
false);
}
|  |
 |  |  |
|
|
|
Der Counter muß nun bei
VideoMachine angelegt und als SalesPoint angemeldet werden. Dies geschieht
in der main -Methode. Direkt unter dem Erzeugen
der Log -Datei wird die Klasse wie folgt
erweitert:
|
|
|
 |  |  |
 |
Counter c = new Counter("Video Rental");
c.attach(new DataBasketImpl());
c.attach(new User("SalespointUser"));
vidMachine.addSalesPoint(c);
|  |
 |  |  |
|
|
|
An den SalesPoint wird ein DataBasket
"angehängt". In diesem Warenkorb werden die Videos des
Kunden nach deren Auswahl so lange aufbewahrt, bis er sie
endgültig ausleiht. Der Nutzer des Counters
muß der Klasse ebenfalls bekannt gemacht werden. Da
User ein Bestandteil des Pakets users ist, werden die
import -Anweisungen von VideoMachine
um eine weitere ergänzt:
|
DataBasketImpl
|
|
|
|
|
Das Angebot kann jetzt betrachtet werden.
|
|
|
|
Hier der Quelltext der in diesem Kapitel geänderten Klassen:
|
|
|
|
|