Logo Logo
InfoHome Themen Projekte Links Software
Themen
JavaHamster
BlueJ
Java
Sprachelemente
Abstrakte Datentypen
Swing
Composite Pattern
AWT > Swing
GUI-Grundstruktur
Farben und Fonts
Layout-Manager
Komponenten 1
Komponenten 2
Komponenten 3
Container
Observer Pattern
Ereignisverarbeitung
MVC-Pattern
Game Of Life
Threads
Aufgaben
Sortieren
HTML
XHTML
CSS
XML
Datenbanken
MySQL
Theoretische Informatik
PHP
Kara
Lego-Roboter
Algorithmen

Composite Pattern

Unter einem Pattern versteht man im allgemeinen ein Entwurfsmuster. Bei der Erstellung graphischer Oberflächern in Java findet das Composite Pattern Anwendung. Die graphische Oberfläche setzt sich aus einfachen Komponenten (=Objekten) zusammen, die in Container-Objekten zusammengefasst werden, die ihrerseits wieder in noch größeren Containern stecken, usw.

Vererbung

Bei der Umsetzung dieses Patterns in Java ist das Vererbungskonzept von zentraler Bedeutung. Eine neu erstellte Klasse kann dabei von einer anderen Klasse abgeleitet (Schlüsselwort extends) werden. Die abgeleitete Klasse erbt alle Funktionalität von der Oberklasse (auch Superklasse genannt), d.h. sie besitzt dieselben Datenfelder und Methoden. Desweiteren lässt sich in der abgeleiteten Klasse zusätzliche Funktionalität unterbringen (weitere Datenfelder und Methoden). Ein fortgesetztes Ableiten führt so zu einer zunehmenden Spezialisierung. In Unterklassen können Methoden aus Oberklassen auch überschrieben werden.

Auf der obersten Stufe der Java-Klassenhierarchie steht die Klasse Object. Jede selbst erstellte Klasse erbt von der Klasse Object wenn nicht eine andere Oberklasse explizit angegeben wird. Die Klasse Object enthält natürlich nur ganz allgemeine Funktionalität (selbst mal in der Java-Dokumentation nachschlagen!).

Ein zusätzliche Besonderheit der Klasse Object ist, dass es sich um eine abstrakte Klasse handelt (Schlüsselwort abstract). Von solchen Klassen lassen sich keine Objekte erzeugen. Erst in abgeleiteten Klassen ist dies dann möglich.

Neben abstrakten Klassen gibt es auch abstrakte Methoden. Eine abstrakte Methode besteht schlicht aus einer Signatur. Enthält eine Klassen eine abstrakte Methode, so ist die Klasse selbst abstrakt. Erst wenn in einer abgeleiteten Klasse die abstrakte Methode implementiert wird, lassen sich Objekte erzeugen.

1. Beispiel: Ableiten ganzrationaler Funktionen

Ganzrationale Funktionen entstehen durch Summen- und Produktbildung aus konstanten Funktionen f(x)=c und der Identität f(x)=x. An diesem Beispiel soll das Composite Pattern nun exemplarisch angewendet werden. Zur Bestimmung der Ableitung an einer bestimmten Stelle x benötigen wir die Ableitungsfunktion und ihren Funktionswert. In unserer abstrakten Oberklasse Funktion sehen wir also zwei Methoden vor, die genau das leisten:

public abstract class Function {

public abstract double getOutput(double x);

public abstract Function getDerivative();

}

Zu beachten ist hier, dass die Methode getDerivative() keinen Wert sondern ein Funktionsobjekt zurückliefert.

Nun kommen wird zu den einfachen Bestandteilen der ganzrationalen Funktionen. Zunächst die Konstanten:

public class Constant extends Function {

private double constant;

public Constant(double constant)
{
this.constant = constant;
}

public double getOutput(double x)
{
return constant;
}

public Function getDerivative()
{
return new Constant(0);
}

}

Die Klasse 'Constant' wird von der Klasse 'Function' abgeleitet und implementiert die vorgesehenen Methoden. Darüberhinaus enthält sie ein Datenfeld zur Speicherung des konstanten Wertes und einen Konstruktor. In einem Hauptprogramm lassen sich nun konstante Funktionen erzeugen, an einer Stelle x auswerten. Möchte man den Wert der Ableitung einer konstanten Funktion f an der Stelle x, schreibt man f.getDerivative().getOutput(x)!

Und jetzt zur Identität:

public class Id extends Function {

public double getOutput(double x)
{
return x;
}

public Function getDerivative()
{
return new Constant(1);
}

}

Nach diesen einfachen Bestandteilen kümmern wir uns um die Klassen für Summe und Produkt, die jeweils aus zwei Objekte vom Typ Funktion zusammengesetzt werden.

public class Sum extends Function {

private Function s1;
private Function s2;

public Sum(Function s1, Function s2)
{
this.s1 = s1;
this.s2 = s2;
}

public double getOutput(double x)
{
return s1.getOutput(x) + s2.getOutput(x);
}

public Function getDerivative()
{
return new Sum(s1.getDerivative(), s2.getDerivative());
}

}

public class Product extends Function {

private Function f1;
private Function f2;

public Product(Function f1, Function f2){
this.f1 = f1;
this.f2 = f2;
}

public double getOutput(double x) {
return f1.getOutput(x) * f2.getOutput(x);
}

public Function getDerivative() {
Function d1 = new Product(f1, f2.getDerivative());
Function d2 = new Product(f1.getDerivative(), f2);
return new Sum(d1,d2);
}

}

Ein Hauptprogramm zum Testen unserer Klassen könnte folgendermaßen aussehen:

public class Main {

public static void main(String[] args) {
// f(x) = 3*x^2 + 7*x + 5

Function f1 = new Constant(3);
Function f2 = new Constant(7);
Function f3 = new Constant(5);
Function f4 = new Id();
Function f5 = new Product(f4,f4);
Function f6 = new Product(f1,f5);
Function f7 = new Product(f2,f4);
Function f8 = new Sum(f6,f7);
Function f9 = new Sum(f8,f3);

System.out.println(f9.getOutput(2));
System.out.println(f9.getDerivative().getOutput(2));
}

}

Das Testprogramm berechnet den Funktionswert an der Stelle x=2 und den Wert der Ableitungsfunktion an der Stelle x=2.

"Ableitung von Funktionen"-Projekt als Anwendung des Composite-Patterns

Die Rolle der allgemeinen Oberklasse spielt die abstrakte Klasse 'Function'. Einfache Klassen sind die Klassen 'Constant' und 'Id'. Zusammengesetzte Klassen sind die Klassen 'Sum' und 'Product'. Statt einer Methode 'add' zum Hinzufügen eines Funktionsobjekts zu einem Objekt einer zusammengesetzten Klasse verwenden wir hier den Konstruktor der Klassen 'Sum' bzw. 'Product', um aus zwei einfachen Objekten ein zusammengesetztes Objekt zu erzeugen.

Ergänzungen

Ergänzung 1: Ausgabe des Ableitungsterms

Um den Term der Funktion (und damit auch jeder Ableitungsfunktion) ausgeben zu können, formulieren wir eine weitere abstrakte Methode printFunction() in unserer Oberklasse 'Function'.

public abstract void printFunction();

Diese Methode muss nun in allen abgeleiteten Klassen implementiert werden.

In der Klasse 'Constant':

public void printFunction(){
System.out.print(constant);
}

In der Klasse 'Id':

public void printFunction(){
System.out.print("x");
}

In der Klasse 'Sum':

public void printFunction(){
System.out.print("(");
s1.printFunction();
System.out.print("+");
s2.printFunction();
System.out.print(")");
}

und in der Klasse 'Product':

public void printFunction(){
System.out.print("(");
f1.printFunction();
System.out.print("*");
f2.printFunction();
System.out.print(")");
}

Klammern nicht vergessen! (Warum?)

Ergänzung 2: Ableitung von Sinus- und Kosinusfunktionen

Stellen Sie die Funktion h(x) = 5*x + sin(3*x + 4) mit Hilfe der in diesem Projekt erstellten Klassen dar. Für die hierbei verwendeten trigonometrischen Funktionen entwerfen Sie zwei neue Klassen 'Sine' und 'Cosine' die entsprechend sin(a(x)) und cos(a(x)) darstellen. Für die Ableitung dieser Funktionen verwenden Sie die aus dem Mathematikunterricht bekannte Kettenregel.

public class Sine extends Function {

Function a;

public Sine(Function a){
this.a = a;
}

public double getOutput(double x) {
return Math.sin(a.getOutput(x));
}

public Function getDerivative() {
return new Product(a.getDerivative(), new Cosine(a));
}

public void printFunction(){
System.out.print("sin(");
a.printFunction();
System.out.print(")");
}
}
public class Cosine extends Function {

Function a;

public Cosine(Function a){
this.a = a;
}

public double getOutput(double x) {
return Math.cos(a.getOutput(x));
}

public Function getDerivative() {
return new Product(new Product(new Constant(-1), new Sine(a)), a.getDerivative());
}

public void printFunction(){
System.out.print("cos(");
a.printFunction();
System.out.print(")");
}
}

Ergänzung 3: Ausgabe einer Wertetabelle

Ergänzen Sie die Klasse 'Function' um die (nicht abstrakte) Methode

public void printTableOfValues(double min, double max, double step)

zur Ausgabe einer Wertetabelle. Dabei geben min und max die Intervallgrenzen an und step die Schrittweite. Testen Sie die neue Methode.

Das gesamte Projekt bis einschließlich Ergänzung 1+2 kann hier heruntergeladen werden.

Ergänzung 4: Summenobjekte mit beliebig vielen Summanden

Ergänzen Sie die Klasse 'Sum' um eine zusätzliche Methode add(Function f), die dem Summenobjekt (nach Erzeugung) einen weiteren Summanden hinzufügt. Verwenden Sie zur Speicherung der Summandenfunktionen den selbstgeschriebenen ADT Liste, eine ArrayList oder ein Vector. Konstruktoren und Methoden müssen dem neuen Design selbstverständlich angepasst werden.

Ergänzung 5: Produktobjekte mit beliebig vielen Faktoren

Ergänzen Sie die Klasse 'Product' um eine zusätzliche Methode add(Function f), die dem Produktobjekt (nach Erzeugung) einen weiteren Faktor hinzufügt. Verwenden Sie zur Speicherung der Faktorenfunktionen den selbstgeschriebenen ADT Liste, eine ArrayList oder ein Vector. Konstruktoren und Methoden müssen dem neuen Design selbstverständlich angepasst werden.
ACHTUNG: Das Ableiten ist nun nicht mehr trivial durch einfache Anwendung der Produktregel möglich !!!

» drucken: pdf | html

© 2004-2024 M. Blanke · Ursulaschule · Kleine Domsfreiheit 11-18 · 49074 Osnabrück