AS3-Tutorium: Flash: Calculator 01 integer

aus GlossarWiki, der Glossar-Datenbank der Fachhochschule Augsburg

Dieser Artikel erfüllt die GlossarWiki-Qualitätsanforderungen nur teilweise:

Korrektheit: 4
(großteils überprüft)
Umfang: 1
(zu gering)
Quellenangaben: 5
(vollständig vorhanden)
Quellenarten: 5
(ausgezeichnet)
Konformität: 5
(ausgezeichnet)

AS3-Tutorium: Calculator: Flash | Flex

Flash: Übersicht | Teil 1 | Teil 2

Taschenrechner

In diesem Teil des Tutoriums wird beschrieben, wie ein einfacher Taschenrechner implementiert werden kann. Dieser kann nur Interzahlen verarbeiten. Auf Komma- und Exponential-Darstellungen wird ebenso verzichtet, wie auf eine Fehlerbehandlung bei einer Division durch Null.

<swf width="300" height="420">http://glossar.hs-augsburg.de/beispiel/tutorium/flash_cs5/calculator/calculator_01_integer/Calculator01Flash11.swf</swf>

Musterlösung Flash (SVN-Repository)

<swf width="300" height="420">http://glossar.hs-augsburg.de/beispiel/tutorium/flex_4/calculator/calculator_01_integer/Calculator01Flex.swf</swf> Musterlösung Flex (SVN-Repository)

Datenmodell

Im Folgenden wird ein Klassendiagramm beschrieben, dass die Implementierung des obigen Taschenrechners gemäß dem LDVCS-Paradigmas ermöglicht. Den für dieses Model von StarUML erzeugten Code-Rahmen finden Sie im Ordner staruml_code der Musterlösung. Beachten Sie allerdings, dass die Attribute im Allgemeinen mit Hilfe von Getter- und Setter-Methoden implementiert werden müssen. Insbesondere für die Attribute data, controller, logic und keyInput, die von der Main-Klasse Calculator01Flash initialisiert werden, sollten nur Setter-Methoden definiert werden. Auf diese Attribute wird niemals von außerhalb zugegriffen.

Medium:AS3-Tutorium Calculator 01 Flash Class Diagram.jpg

Vier LDVCS-Komponenten

Aus folgenden vier LDVCS-Komponenten besteht der Taschenrechner (eine Service-Komponente gibt es nicht, da der Taschenrechner weder konfiguriert werden muss noch mit anderen Anwendungen kommuniziert):

ViewCalculator
Die View-Komponente enthält ein Panel bestehnd aus Digitalziffern (ein Vorzeichen-Element und acht Sieben-Segment-Elemente) zum Darstellen des Rechenergebnisses. Außerdem enthält sie 16 Tasten zur Eingabe von Ziffern und Operatoren.
ControllerCalculator
Der Controller bietet eine Methode keyClick, die von der View aufgerufen wird, sobald eine Taste gedrückt wird. Dieser Methode wird im Parameter p_value übergeben, welche Taste gedrückt wurde: 0, ..., 9, C, =, +, -, ×, ÷. Der Controller überfügt über zwei Attribute, die bei Programmstart initialisiert werden:
  • stage enthält das Stage-Objekt, um auf Tastatur-Ereignisse direkt zugreifen zu können. Damit kann der Taschenrechner auch über die Tastatur gesteuert werden.
  • logic enthält die Logik-Komponente der Anwendung. Die Aufgabe des Controllers ist es, die Methoden dieser Komponenten abhängig von der Benutzer-Eingabe aufzurufen.
LogicCalculator
Aufgabe der Logik-Komponente ist es, abhängig von der Benutzeraktion ein neues Ergebnis zu berechnen und dieses im Data-Objekt zu speichern.
Es gibt drei mögliche Benutzeraktionen:
  • Drücken einer Ziffer: addDigit
  • Drücken einer Operatortaste: addOperator
  • Drücken der Clear-Taste: clear
DataCalculator
Das Daten-Objekt enthält diejenigen Daten die von der View visualisiert werden können. Für den in der Musterlösung vorgestellten Taschenrechner braucht das Daten-Objekt nur eine einziges Atribut zu enthalten:
  • value: Die Zahl, die aktuell erfasst wird bzw, das Ergebnis, nachdem eine mathematische Operation ausgeführt wurde.

Es ist aber durchaus sinnvoll, weitere Attribute für die Visualisierung bereitzustellen (siehe oben: Musterlösung Flex):

  • operator: Der Operator, der als nächstes angewendet werden soll (wenn die aktuelle Zahl vollständig eingegeben wurde).
  • oldValue: Die letzte Zahl, die erfasst bzw. gerechnet wurde. Diese wird mit der aktuellen Zahl (mit Hilfe des zuvor eingegebenen Operators) verknüpft, sobald der nächste Operator ("=", "+", "-", "×", "÷") gedrückt wird.

Separation of Concerns

Jede der im Diagramm angegebenen Klassen hat genau eine Aufgabe (vgl. ProgrammierprinzipenSeparation of Concernse“ und „Single responsibility principle“):

LED

Visualisierung einer LED (Leuchtdiode), die ein- und ausgeschaltet werden kann.

LEDRed, LEDPlusRed, LEDMinusRed

Flash-Symbole, die rote LEDs visualisieren und LED als Basisklasse haben. (Man könnte auf die Klassen LEDPlusRed und LEDMinusRed auch verzichten, wenn LEDRed rechteckig ist. Dann kann man ein Plussymbol einfach durch zwei LEDs symbolisieren.)

Sign

Visualisierung eines Vorzeichen-Elements, bestehend aus ein bis drei LEDs. In der Musterlösung kann das Element drei Werte annehmen: „positiv“, „negativ“ und „ausgeschaltet“. Es würde aber auch reichen, nur ein Minuszeichen als Vorzeichen zu verwenden. In diesem Fall würden sich die Zustände „positiv“ und „ausgeschaltet“ bei der Visualisierung nicht unterscheiden (siehe oben: Musterlösung Flex).

Digit

Visualisierung eines Sieben-Segment-Elements, bestehend aus sieben LEDs. In der Musterlösung kann ein deratiges Element ide Ziffern 0 bis 9 darstellen sowie ausgeschaltet sein. Weitere Werte (wie z.B. ein E für Error) sind denkbar.

Panel

Visualisierung einer Intergerzahl mit maximal acht Stellen.

Key

Visualisierung eines Taschenrechner-Buttons für die Benutzereingabe. In der Musterlösung wurde allerdings nicht Button als Basisklasse verwendet, sondern MovieClip, da die Skinning-Möglichkeiten von Flash-Buttons eingeschränkt sind. In der Flex-Musterlösung wurden dagegen Flex-Spark-Buttons verwendet. Deren Aussehen kann problemlos gemäß den eigenen Wünschen verändert werden (Skinning).

ViewCalculator
Verknüpfung der anderen Komponenten mit der View:
  • Das Panel muss den Wert des Attributs value der Daten-Komponente darstellen.
  • Die Keys müssen, sobald sie geklickt werden, ihren Label-Wert an die Methode keyClick der Controller-Komponenten weiterleiten.
ControllerCalculator
Aufbereitung der Benutzer-Eingaben für die Logik-Komponenten. Dabei werden zwei Arten von Eingabe unterstützt:
  • Eingabe per Maus-Klick (View-Komponente)
  • Eingabe per Tastatur (Listener für Keyboard-Events)
LogicCalculator
Aktualisieren der Werte in der Daten-Komponente nach jeder Benutzerkation.
DataCalculator
Verwaltung der Daten, die visualisiert werden sollen:
  • Speichern der Daten
  • Informieren der interessierten Observers über Änderungen
Calculator01Flash
Starten der Anwendung, insbesondere Erzeugen und Initialisieren aller LDVCS-Komponenten.
EnumKey
Bereitstellung von Konstanten, die im Controller, in der Logikkomponente sowie im Daten-Objekt verwendet werden.

Anmerkung: Das Prinzip ProgrammierprinzipSingle responsibility principle“ wurde nicht zu 100% umgesetzt. So könnten z.B. zwei Controller-Komponenten definiert werden, eine für die Verarbeitung von Bildschirmeingaben und eine für die Verarbeitung von Tastatureingaben. Genauso könnte die Klasse ViewCalculator durch zwei Klassen ersetzt werde: Eine zur Initialisierung der Daten-Ausgabe (Panel) und eine zur Initialisierung der Benutzereingabe (Keys).

Ziel des „Single responsibility principle“ ist immer, dass es für keine Klasse mehrere Gründe geben soll, warum sie geändert werden muss. Zum Beispiel muss derzeit die Klasse ControllerCalculator geändert werden, wenn sich die Tastatureingabe ändert (was z.B. bei einem Umstieg von einer deutschen auf eine französische Tastatur notwendig wird, da der KeyboardEvent nur die Character Codes einer englischen Tastatur liefert und daher die Controller-Klasse an jede Tastatur angepasst werden muss). Aber auch, wenn die Symbole der Tasten, die per Maus aktiviert werden, geändert werden sollen, muss diese Klasse modifiziert werden, obwohl der eine Änderungswunsch nichts mit dem anderen zu tun hat.

Implementierung

LED: Visualisierung einer LED (Leuchtdiode)

Diese Klasse enhält ein Attribut value, das die beiden Werte LED.OFF und LED.ON annehmen kann.

Das Attribut wird mit Hilfe eines Getter-/Setter-Paares realisiert:

public function get value(): int {...}
public function set value(p_value: int): void {...}

Die Getter-Methode liefert als Ergebnis den aktuellen Status der LED. Dieser wird aus einer privaten Zustandsvariablen ausgelesen. Auf diese Methode könnte sogar verzichtet werden, da es nicht die Aufgabe einer View ist, Auskunft über seinen Zustand zu geben. Eine View dient lediglich dazu, einen Zustand zu visualisieren. Allerdings ist die Getter-Methode für das Debugging vorteilhaft, um den aktuellen Wert, den die LED visualisieren soll, per trace auszugeben.

Sofern die Getter-Methode implementiert wurde, muss die Setter-Methode den neuen Wert in der zuvor genannten Zustandsvariablen speichern. Ihre eigentliche Aufgabe ist jedoch, den neuen Wert zu visualisieren, indem sie das zugeörige Objekt this (dem die Klasse LED als Basisklasse zugeordnet ist) manipuliert. Dafür gibt es mehrere Möglichkeiten:

  • die Eigenschaft visible des Symbols auf true oder false setzen
  • den Alphawert des Symbols verändern
  • zu einer dem Wert ensprechenden Stelle in der Timeline des Symbols springen


Sign und Digit: Visualisierung eines Vorzeichen-Elements und eines Sieben-Segment-Elements

Diese Klassen enhalten ein Attribut value, das diverse Werte annehmen kann. Wie zuvor wird es mit einem Getter-/Setter-Paar realisiert, wobei auf die Getter-Methode wieder (aus demselben Grund wie zuvor) verzichtet werden könnte:

public function get value(): int {...}
public function set value(p_value: int): void {...}

Die wichtigste Aufgabe der Setter-Methode ist die Visualisierung des übergebenen Wertes mit Hilfe von LEDs. Dafür werden in einem zugehörigen Symbol (das heißt, in einem Symbol, dem die Klasse Sign bzw. Digit als Basisklasse zugeordnet wurde) LED-Symbole eingefügt und mit „Instanznamen“ (wie z.B. sp_led_0, sp_led_1, ...) versehen. Die Aufgabe der Setter-Methode ist es nun, die LEDs (zum Beispiel mit Hilfe einer Schleife) zu initialisieren:

private static const c_number_of_leds: uint = ...;
private static const c_led_prefix: String = "sp_led_";
private static const c_led_states: Array = [ [LED.ON, LED.OFF, ...], 
                                             [LED.OFF, LED.ON, ...], 
                                             ...
                                           ];

for (var i: int = 0; i < c_number_of_leds; i++)
  this[c_led_prefix + i].value = c_led_states[p_value+1][i];

Die Konstante c_led_states enthält ein Array von Arrays. Für jeden möglich value-Wert ist im äußeren Array ein Array enthalten, welches für jede LED beschreibt, ob sie ein- oder ausgeschaltet werden muss, wenn dieser Wert visualisiert werden soll.

Ein Sieben-Segment-Element enthält sieben LEDs. Das Vorzeichen-Element enthält ein bis drei LEDs:

  • ein Minus-Symbol (hier besteht kein Unterschied zwischen POSITIVE und OFF)
  • ein Minus- und ein Plus-Symbol
  • ein Minus- und ein Plus-Symbol, wobei letzteres aus zwei LED zusammengesetzt wird


Panel: Visualisierung einer Intergerzahl

Die Aufgabe eines Panel-Objektes ist es wiederun eine Integerzahl zu visualisieren. Diesmal soll jedoch nicht nur eine Ziffer, sondern eine ganze Zahl (einschließlich Vorzeichen) dargestellt werden. Dazu werden dem Panel ein Vorzeichenelemnt (sp_sign) sowie acht Ziffern-Elemente zugeordnet: sp_digit_7 bis sp_digit0; das niederwertigste Element befindet sich ganz rechts.

Wie zuvor gibt es eine Getter- und eine Setter-Methode für den Zugriff auf das Attribut value (wobei die Getter-MEthode wiedrun nicht unbedingt nötig ist):

public function get value(): int {...}
public function set value(p_value: int): void {...}

Die Implementierung der Setter-MEthode ist allerdings etwas aufwändiger als zuvor.

Zunächst muss der übergebene Wert gesichert werden (sofern man die Getter-Methode implementiert):

v_value = p_value;

Als nächstes muss sichergestellt werden, dass der übergebene Wert nicht zu groß oder zu klein ist, um die Zahl mit acht Ziffern darzustellen. Falls dies nicht der Fall ist, wird der übergebene Wert durch die größte bzw. kleinste darstellebare Zahl ersetzt (wobei c_max_value == Math.pow(10,8)-1 gilt):

if (p_value < -c_max_value)
  p_value = -c_max_value;
else if (p_value > c_max_value)
  p_value = c_max_value;

Nun kann der Wert des Vorzeichens bestimmt werden. Als Seiteneffekt wird p_value durch seinen Absolutbetrag ersetzt, damit für die nachfolgenden Befehle p_value >= 0 garantiert werden kann:

if (p_value > 0)
  sp_sign.value = Sign.POSITIVE;
else if (p_value < 0)
{
  sp_sign.value = Sign.NEGATIVE;
  p_value = -p_value;
}
else
 sp_sign.value = Sign.OFF; // 0 hat kein Vorzeichen!

Als nächstes werden die Ziffern-Elemente (von rechts nach links) auf die korrekten Werte gesetzt, wobei führende Nullen nicht beachtet werden. Die do-while-Schleife garantiert, dass die Zahl Null nicht mit null Ziffern, sondern mit einer Null visualisiert wird.

var i: int = 0;
      
do
{
 this[c_digit_prefix + i].value = p_value % c_base;
 p_value /= c_base;
 i++;
}
while (p_value > 0);

Zu guter Letzt werden die restlichen Ziffern ausgeschaltet.

while (i < c_number_of_digits)
{
  this[c_digit_prefix + i].value = Digit.OFF;
  i++;
};

Key: Visualisierung eines Taschenrechner-Buttons

Dieser Artikel wird derzeit von einem Autor gründlich bearbeitet. Die Inhalte sind daher evtl. noch inkonsistent.


ViewCalculator: Verknüpfung der anderen Komponenten mit der View


EnumKey: Bereitstellung von Konstanten


ControllerCalculator: Aufbereitung der Benutzer-Eingaben für die Logik-Komponenten


LogicCalculator: Aktualisieren der Werte in der Daten-Komponente


Mealy-Automat Medium:AS3-Tutorium Calculator 01 Mealy Machine.jpg


DataCalculator: Verwaltung der Daten, die visualisiert werden sollen


Calculator01Flash: Starten der Anwendung, insbesondere Erzeugen und Initialisieren aller LDVCS-Komponenten

Quellen

SVN-Repository-Verweise


Dieser Artikel ist GlossarWiki-konform.