Wir werden im weiteren Verlauf die Klassen Potenz, Produkt und Summe einführen, die alle genau zwei Operanden besitzen. Deshalb ist es sinnvoll, dafür einen abstrakten Vorfahr zu modellieren: "ZweiOperanden"
oder als Quelltext:
abstract class ZweiOperanden extends Term{
protected Term operand1, operand2;
}
Das Schlüsselwort protected bedeutet auch hier, dass nur Klassen, die von "ZweiOperanden" abgeleitet sind auf diese Attribute zugreifen können.
Aufgabe:
- Erstellen Sie ein UML-Diagramm der Klasse "Potenz" und schreiben Sie, soweit es Ihnen möglich ist, den Quelltext dazu.
Hier das komplette bisherige UML-Diagramm:
abstract class Term {
public abstract double berechne_wert( double belegung[] );
public abstract String als_text();
}
class Zahl extends Term{
private double wert; // Eigenschaft
public double berechne_wert( double belegung[] ){ // Methode
return wert; // zur Berechnung des Wertes
}
public String als_text(){
return Double.toString( wert );
}
public Zahl( double wert ){
this.wert = wert;
}
}
class Variable extends Term{
private Character buchstabe;
public double berechne_wert( double belegung[] ){
int pos = buchstabe.compareTo( new Character( 'a' ) );
return belegung[pos];
}
public String als_text(){
return buchstabe.toString();
}
public Variable( char start_buchstabe ){
buchstabe = new Character( start_buchstabe );
}
}
abstract class Funktion extends Term{
protected Term argument;
}
class Ln extends Funktion{
public Ln( Term start_argument ){
argument = start_argument;
}
public double berechne_wert( double belegung[] ){
return Math.log( operand.berechne_wert( belegung ) );
}
public String als_text(){
return "ln("+operand.als_text()+")";
}
}
abstract class ZweiOperanden extends Term{
protected Term operand1, operand2;
}
class Potenz extends ZweiOperanden{
public Potenz( Term startoperand1, Term startoperand2 ){
operand1 = startoperand1;
operand2 = startoperand2;
}
public double berechne_wert( double belegung[] ){
return Math.pow( operand1.berechne_wert( belegung ), operand2.berechne_wert( belegung ) );
}
public String als_text(){
return operand1.als_text()+"^"+operand2.als_text();
}
}
Aufgabe:
- Erstellen Sie Quelltext im Unterprogramm "main", der Ihre neue Klasse testet.
Aufgabe:
- Welche Ausgabe erzeugt der folgende Quelltext?
public static void main ( String args[] ){
double belegung[] = { 0.0 }; // wird nicht berechnet, da keine Variablen verwendet werden
Term p = new Potenz( new Zahl(2), new Potenz( new Zahl(3), new Zahl(2) ) );
System.out.println( "Textdarstellung der Potenz: "+p.als_text()+"="+p.berechne_wert(belegung) );
Term q = new Potenz( new Potenz( new Zahl(2), new Zahl(3)), new Zahl(2) );
System.out.println( "Textdarstellung der Potenz: "+q.als_text()+"="+q.berechne_wert(belegung) );
}
Hier werden die Terme p und q auf verschiedene Weisen erzeugt p = 23² und q = (2³)2. Dieser Unterschied wird aber in der Textdarstellung der Terme nicht ersichtlich, da keine Klammern gesetzt wurden.
Wie kann man nun der Ausgabe beibringen, dass eine Klammer gesetzt werden soll?
Offenbar ist immer dann eine Klammer notwendig, wenn der Teilterm kein Atom (also Zahl oder Variable) ist. Dazu muss also eine Methode geschrieben werden, die entscheidet, ob ein Term ein Atom ist.
Aber zu welcher Klasse gehört der Quelltext? Das Unterprogramm soll einen Term übergeben bekommen und anschließend einen Wahrheitswert ausgeben. Es handelt sich um ein Unterprogramm, das zu keiner konkreten Klasse gehört, aber allgemein mit Termen zu tun hat. Deshalb bietet es sich an, es in der Klasse "Term" für alle Aufrufer zur Verfügung zu stellen:
class Term{
...
public static boolean ist_atom( Term t ){
return t.getClass() == Zahl.class || t.getClass() == Variable.class;
}
...
}
Das Zauberwort static bedeutet, dass dieses Unterprogramm aufgerufen werden kann, ohne dass es zu einem konkreten Objekt der Klasse Term gehört. Stattdessen könnte ein Aufruf z.B. so aussehen:
Term z = new Zahl( 2 );
System.out.println( Term.ist_atom( z ) );
Ein Aufruf erfolgt dann in der Klasse "Potenz":
public String als_text(){
String operand1_text = operand1.als_text();
if (!ist_atom( operand1 ) ) operand1_text = "(" + operand1_text + ")";
String operand2_text = operand2.als_text();
if (!ist_atom( operand2 ) ) operand2_text = "(" + operand2_text + ")";
return operand1_text+"^"+operand2_text;
}
Aufgabe:
- Die Routine als_text() enthält immer noch eine Menge Redundanz. Verbessern Sie dies, indem Sie in die abstrakte Klasse "ZweiOperanden" eine statische Methode public String operand_als_text( Term op ) einführen, die Klammern um den Term setzt, wenn er kein Atom ist.
Wenn Sie die Aufgabe gelöst haben, dann ergibt sich folgender Quelltext:
public String als_text(){
String operand1_text = operand_als_text( operand1 );
String operand2_text = operand_als_text( operand2 );
return operand1_text+"^"+operand2_text;
}