Java Prädikate und Strukturmuster

PGJ

Lt. Junior Grade
Registriert
Mai 2005
Beiträge
398
Hallo sehr geschätzte Computerbase Community,

in meinem Informatikstudium verstehe ich folgende Aufgabe nicht ganz:
Mit Hilfe von Prädikaten können Sie Objekte auf bestimmte Eigenschaften prüfen oder
Mengen filtern. Prädikate lassen sich mit den booleschen Operatoren UND, ODER und
NICHT verknüpfen. Bitte gehen Sie davon aus, dass ein konkretes Prädikat folgende
Schnittstelle bereitstellt:

package de.wwu.se;

public interface Praedikat {

boolean istErfuellt(Object kandidat);

Praedikat und(Praedikat anderes);
Praedikat oder(Praedikat anderes);
Praedikat nicht();​
}

a) Um Prädikate mit den booleschen Operatoren darstellen zu können, verwenden Sie
bitte ein geeignetes Strukturmuster! Bitte benennen Sie das Strukturmuster
und erstellen Sie für den gegebenen Sachverhalt ein UML Klassendiagramm mit
dem Modellierungstool Papyrus oder einem UML Modellierungstool ihrer Wahl!

b) Implementieren Sie Java-Klassen für die Prädikate und überschreiben Sie dabei die
Methode public String toString() jeweils so, dass eine Beschreibung des
vorliegenden Prädikats erzeugt wird!

c) Schreiben Sie eine kurze Testmethode, die die von Ihnen implementierte Funktionalität testet! Dabei sollen Prädikate, die Strings auf bestimmte Eigenschaften
(bspw. Mindestlänge, ausschlieliche Verwendung von Zahlen, usw.) testen, erzeugt
werden. Bitte lassen Sie außerdem die Beschreibung der Prädikate ausgeben.

Da ich leider nicht sonderlich gut in Java bin und zudem das Semester sich mehr um Grafiken und Abläufe als um Programmierung drehte, fällt es mir schwer die Aufgabe zu lösen. Es wäre echt cool, wenn ihr mir ein bisschen auf die Sprünge helfen könntet. Und ihr müsst mich schon fast als Java-DAU ansehen.

Z. B. die Zeile "package de.wwu.se;": Was bedeutet das genau? Wird ein Package aus dem Internet geladen oder wie? Inwiefern ist dieses relevant?

Zu a): Kennt jemand ein gutes UML Programm? Papyrus (in Eclipse) habe ich ausprobiert, aber komme damit nicht wirklich klar. Ansonsten würde ich Visio verwenden. Und handelt es sich hierbei um das Muster eines Dekorierers? Kennt jemand eine gute Seite, die die Strukturmuster gut erläutert? Denn in der Vorlesung kam das Thema doch etwas knapp und nicht sonderlich ausführlich vor.

Zu b): Wie überschreibt man die Methode toString? Und muss ich das für jede Unterscheidung (Und, oder sowie nicht) machen? Ich verstehe nicht ganz was b) bedeuten soll.

Zu c): Okay, das wäre dann also irgendeine main Methode zum Testen des Ganzen. Aber verstehe ich das richtig, dass ich dann die einzelnen Prädikate doch irgendwie implementieren muss, damit ich diese überhaupt testen kann?


Leider bin ich nicht sonderlich fit in Java... daher wäre ich um jeden Hinweis, weiterführenden Link oder Tipp sehr dankbar!
 
Erster Powertipp: Setze dich mit deinen Kommilitonen zusammen. Gruppenlernen macht mehr Spass.
Zweiter Powertipp: Du bist Student und dazu gehört zu wissen, wo es welche Informationen gibt ohne auf die Hilfe von Dritten angewiesen zu sein. (Computerbase ist kein Hausaufgaben Board)

zu Package: http://lmgtfy.com/?q=java+package
zu a) http://lmgtfy.com/?q=design+patterns
zu b) http://lmgtfy.com/?q=Polymorphism
zu c) Du musst schon implementieren und Tests dafür schreiben. Unter Java wird JUnit (http://junit.sourceforge.net/) hauptsächlich dafür verwendet.
 
Oh je, du scheinst ja keine Peilung zu haben. Hattet ihr noch kein Java oder was? Java ist auch eine Insel wird häufig empfohlen, aber es gibt doch sicherlich auch Literaturempfehlungen im Rahmen der Vorlesung. Setz dich erstmal mit der Literatur auseinander.

PS: Bei de.wwu.se steht das wwu vermutlich für die Westfälische Wilhelms-Universität Münster, (an der ich auch studiere) das sollte man als Student wissen! Nicht www, das hat nix mit Internet zu tun ;)
 
Grundsätzliches: Ich rate dir dringend, ein Java-Buch von vorne bis hinten zu lesen. Ich habe es noch nie verstanden, wieso viele Kommilitonen sich über viele Semester hinweg mit übelster Frickelei durch zahlreiche Übungen quälen, anstatt einfach mal systematisch die jeweilige(n) Sprache(n) zu lernen. Ich denke, insgesamt ist der Aufwand geringer, einmal ein gutes Buch zu lesen, als tausendmal im Internet nach Codeschnippseln zu suchen.

Packages: In Java werden Namensräume von Packages umgesetzt. Mit dem Schlüsselwort package wird der Namensraum definiert, in dem die Klassen dieser Datei liegen. package ist also dem C++-Schlüsselwort namespace ähnlich. (Falls du C++ kennst.) Fremde Namensräume werden in Java dann mit dem Schlüsselwort import bekannt gemacht (ähnlich using in C++). Das wird dir auch schnell begegnen.

a) Es gibt keine guten UML-Programme. Als das kleinste Übel sehe ich Visual Paradigm an. Und ja, es müsste der Dekorierer gemeint sein.

b) Man überschreibt Methoden, indem man sie in Unterklassen einfach neu definiert, gerade so als ob man sie zum ersten Mal definieren würde. An der Signatur darfst du natürlich nichts ändern.

c) Natürlich musst du die Prädikate implementieren. Bei deinem Kenntnisstand kannst würde ich den Test allerdings einfach als main-Methode implementieren und nicht das JUnit-Framework verwenden. Es sei denn natürlich, dass dies explizit behandelt wurde.
 
Packages haben "umgekehrte Domainnamen".

Also, jemand der bei google arbeitet, zb am Projekt supergeilesProjekt.google.de würde das Paket zu der Software "de.google.supergeilesProjekt.*" nennen.

Wenn also dein Package "package de.wwu.se;" heißt, kannst du davon ausgehen, dass eventuell unter der Adresse "se.wwu.de" etwas mehr Infos verfügbar sind. Kann sein, muss aber nicht.

In dem Fall scheint es nicht zu funzen. Also ist es eine logische Gliederung, das heißt, das Package hat wohl der Lehrstuhl Software Engineering der Westfälischen Wilhelms-Universität entwickelt.

Dagegen kann man sein Package natürlich auch gegen alle Konventionen nennen:
"diesIstMeinSuperGeilesPackageDasGegenAlleKonventionenVerstoesst"
 
Zuletzt bearbeitet:
Erstmal danke für eure Unterstützungen, Tipps und Links.
Dass sich hier um kein Lösungsforum handelt, weiß ich und ich verlange auch nicht, dass jemand mir die Aufgaben löst. Z. Zt. lese ich wieder "Java ist auch eine Insel", um in das Thema wieder einzusteigen. Ich würde ja gerne mit meinen Team-Kollegen die Aufgaben behandeln, jedoch sind die jetzt schon seit längerer Zeit nicht erreichbar... bevor gar nichts zustande kommt. ergreife ich die Initiative.

Zu Aufgabenteil a) habe ich ein Strukturmuster eines Dekorierers erstellt. Dabei kann mir doch bestimmt jemand sagen, ob das korrekt oder verbesserungswürdig ist. (Habe die Grafik mit yEd erstellt. Kann ich vielen empfehlen).


Für weitere Hilfestellungen bin ich immer dankbar!
 
Also irgendwie verstehe ich dein UML-Diagramm nicht ganz.
Ich war doch tatsächlich der Meinung, dass es IN der Klasse 'Praedikat' eine Methode geben soll, die und() heißt, eine die nicht() heißt und eine, die oder() heißt.

Dazu gibt es eine Methode isterfuellt() die einen Bool-Wert zurückgibt.
Warum du das jetzt so seltsam aufgebaut hast, verstehe ich nicht. Vielleicht liege ich auch falsch - aber SO macht dein UML-Diagramm keinen Sinn.

Die KonkreteTestklasse ist einfach nur eine Klasse, die mit n beliebigen Prädikaten einfach nur ein und/oder/nicht ausführt. Warum der Rest hier mit JUnit kommt ist mir ebenso ein Rätsel. Dafür braucht man kein Framework, da reicht eine simple, einfache Implementierung mit konkreten Daten.
 
voodoo44 schrieb:
Also irgendwie verstehe ich dein UML-Diagramm nicht ganz.
Ich war doch tatsächlich der Meinung, dass es IN der Klasse 'Praedikat' eine Methode geben soll, die und() heißt, eine die nicht() heißt und eine, die oder() heißt.

Dazu gibt es eine Methode isterfuellt() die einen Bool-Wert zurückgibt.
Warum du das jetzt so seltsam aufgebaut hast, verstehe ich nicht. Vielleicht liege ich auch falsch - aber SO macht dein UML-Diagramm keinen Sinn.

Die KonkreteTestklasse ist einfach nur eine Klasse, die mit n beliebigen Prädikaten einfach nur ein und/oder/nicht ausführt. Warum der Rest hier mit JUnit kommt ist mir ebenso ein Rätsel. Dafür braucht man kein Framework, da reicht eine simple, einfache Implementierung mit konkreten Daten.

Ich bin einfach von der Grundstruktur eines Dekorierers ausgegangen: http://de.wikipedia.org/wiki/Decorator
 
Ja, du sollst doch aber mMn den Dekorierer an Hand eures Beispiels darstellen?
"erstellen Sie für den gegebenen Sachverhalt ein UML Klassendiagramm"
 
Dein Diagramm ergibt aus mehreren Gründen keinen Sinn. Der offensichtliche ist wohl, dass es Testklassen enthält. Testklassen sind - wer hätte es gedacht - zum Testen der Anwendung dar. Sie sind jedoch keinesfalls selbst ein Teil der Umsetzung des Entwurfsmusters. Ich hab die Testklasse im Diagramm unten für dich auch mal eingezeichnet. Sie stellt jedoch keinen Teil des eigentlichen Musters dar, sondern ist nur ein Beispiel für einen beliebigen Klienten, der die Prädikate eben verwendet.

Ich finde die Aufgabenstellung nicht ganz glücklich gewählt. In der Realität würde ich es etwas anders machen, aber das hier wäre mein Lösung der Aufgabe. Rein von der Struktur her könnte es zwar als Dekorierer durchgehen. Von der Funktionalität her würde ich es aber nun doch eher als Kompositum führen wollen.



zeichnung1j.png



Edith: Hier sind noch ein paar recht schöne Ausführungen zum Thema Dekorierer vs. Composite: http://stackoverflow.com/questions/...n-the-composite-pattern-and-decorator-pattern
 
Zuletzt bearbeitet:
Bin jetzt auch kein UML Profi, aber kann man das wirklich so modellieren? Sieht mir mehr aus, als wären die Methoden des Interfaces bei dir Klassen. Außerdem verstehe ich nicht, was du mit der abstrakten Klasse bezweckst.

Also Dekorierer ist hier fehl am Platze, höchstens Composite.

Hab mir das ganze gerade mal in Java implementiert. Das Hauptproblem ist meiner Meinung nach, dass Programmiersprachen von außen nach innen auswerten, Logische Formeln aber eigentlich von innen nach außen. Ich persönlich hätte das Interface anders gestaltet, wobei, vielleicht habe ich auch falsch verstanden, wie der Konstrukteur das verwenden möchte?

Audrücke der Form:

if (OnlyNumbersString.nicht().nicht().und(OnlyOneToFiveNumbersString).istErfuellt("123")) {...}

bringen jedenfalls gewaltige Probleme mit sich.

Vielleicht hätte man bei der Aufgabe ein Beispiel mitgeben sollen. Vielleicht soll man es anders verwenden, erschließt sich mir nicht.
 
Sieht mir mehr aus, als wären die Methoden des Interfaces bei dir Klassen.

Den Satz fand ich schon in deinem vorherigen Beitrag verwirrend. Die Methoden sind bei mir Klassen? Die Aussage ergibt irgendwie keinen Sinn. Die Methoden and, not und or können ja auch weiterhin Member von Predicate sein, wieso auch nicht? Die Frage ist doch: Wie willst du prädikatenlogische Formeln im Speicher repräsentieren. Ich hätte es jetzt über einen Objektbaum gemacht. Das folgende Bild zeigt den Objektbaum für die Formel

Code:
( ! ( ! IsNumeric ^ HasMinLength ) v ContainsSpecialChar ) ^ ! IsUpperCase
zeichnung1.png


Das ist übrigens ganz grob vereinfacht derselbe Ansatz, wie auch Hibernate zusammengesetzte Prädikate repräsentiert. Dort hast du die Klasse BooleanExpressionPredicate, deren Predicate.BooleanOperator entweder AND oder OR ist. Dieses BooleanExpressionPredicate kann sich auf beliebig viele andere Predicates beziehen, welche eben mit dem angegebenen BooleanOperator verknüpft werden. Daneben gibt es eine Reihe von "Leaf-Predicates" (ich nenne sie jetzt einfach mal so, in Anlehnung ans Composite-Muster). Diese beziehen sich nicht auf weitere Predicates, sondern modellieren eben ein "Basisprädikat" wie z. B. IsEmptyPredicate.

In Hibernate hast du übrigens auch im CriteriaBuilderImpl die Methoden and, not und or, obwohl du auch Klassen hast, die And- und Or-Prädikate repräsentieren, wie gesagt: Warum auch nicht?

Wenn du keine And-/Or-/Not-Prädikate haben willst, wie setzt du denn dann zum Beispiel folgende Formel um:

Code:
! ( ! IsNumeric ^ ! HasMinLength )

Wenn du nun nur zwei Objekte IsNumericPredicate und MinLengthPredicate, aber kein AndPredicate hast, dann wüsste ich nicht, wie man dies auch nur halbwegs sinnvoll implementieren soll. Wie unterscheidest du denn z. B. die Negation, die sich auf beide Prädikate bezieht von den Negationen, die sich nur jeweils auf eines der Prädikate beziehen?


Außerdem verstehe ich nicht, was du mit der abstrakten Klasse bezweckst.

Faulheit. Wir haben zwei Typen von Prädikaten: Leaf-Prädikate und Composite-Prädikate. IsNumericPredicate und MinLengthPredicate sind Prädikate für sich alleine ("Leaf-Prädikate"). And- und Or-Prädikate ergeben ja für sich genommen keinen Sinn, sondern müssen sich auf andere Prädikate beziehen ("Composite-Prädikate"). Und da ich jetzt nicht von allen Composite-Prädikaten den blöden Aggregationspfeil zum Interface zeichnen wollte, steht da noch die abstrakte Oberklasse.
 
Was ich aus der Aufgabe lesen kann, ist halt das Interface mit den Methoden, das finde ich halt bei dir nicht.

p.jpg

Deinen Beispielausdruck verstehe ich leider auch nicht. Leere Prädikate also der Form

Praedikat bzw. Praedikat()

sind streng genommen nicht sinnvoll, da man dann wieder bei klassischer Bool Logik ist der Form (x v y).

Ein Prädikatenlogischer Ausdruck heißt doch "IsNumberic(string)", aber nicht "IsNumeric".
 
Zuletzt bearbeitet:
Bei dem Interface Predicate handelt es sich doch genau um das Interface aus der Aufgabe - einschließlich der vier Methoden. Wie du sicherlich bemerkt hast, habe ich der Übersichtlichkeit halber auf die Aufzählung von Methoden generell verzichtet. Ich verstehe auch nicht, warum du dich daran nun störst: Klassendiagramme, die nur Klassennamen und nicht auch noch alle Attribute und Methoden enthalten, sind mehr als üblich.

Dass sich die Prädikate auf eine Zeichenkette beziehen, habe ich als selbstverständlich angenommen. Alles andere ergibt nun wirklich gar keinen Sinn. Da jedes Prädikat aber exakt dieselbe Argumentliste hat (nämlich die eine zu überprüfende Zeichenkette), dachte ich, die Sachlage sei auch ohne den Zusatz (string) hinter jedem Prädikat klar.

Nachdem nun alle Spitzfindigkeiten geklärt wurden, können wir ja mal über die eigentlichen Fragen reden: Wie baust du prädikatenlogische Ausdrücke sinnvoll im Speicher auf - ohne dedizierte Objekte für boolesche Verknüpfungen? Wie repräsentierst du zum Beispiel eine negierte Konjunktion von zwei ebenfalls jeweils negierten Prädikaten? Wie unterscheidest du dabei zwischen der Negation über beide Prädikate und den beiden Negationen über jeweils eines der Prädikate?
 
Eine Negation an sich ist eben nicht Teil der modellierten Welt. bzw, kann sie nicht sinnvoll das Interface Praedikat implementieren. Das gilt auch für "und" und "oder". Das heißt, dort würde ich auf die Java Funktion zugreifen (dein Beispiel):

!(Praedikat1.nicht().istErfuellt(kandidat1) && Praedikat2.nicht().istErfuellt(kandidat2))

Letztlich ist es "defective by design" sowas in Java zu implementieren. Daher sollte man es sich nicht noch schwerer machen.

Wie man es elegant löst, da bin ich mir momentan auch nicht 100% sicher.

Edit: Falls der Kandidat derselbe sein soll, könnte man es auch so machen:

!(Praedikat1.nicht().und(Praedikat2.nicht()).istErfuellt(kandidat))

äquivalent zu umgekehrter auswertung der Prädikate, also vertauschen von 1 und 2.
 
Zuletzt bearbeitet:
Eine Negation an sich ist eben nicht Teil der modellierten Welt. ... Das gilt auch für "und" und "oder".

Nun muss ich mal auf die Aufgabe verweisen. Ich sehe da eine Methode, die offenkundig eine Negation des aufgerufenen Prädikates zurückgeben soll. Ebenso sehe ich eine Methode, die eine Konjunktion/Disjunktion des aufgerufenen Prädikats mit dem übergebenen Prädikat zurückgeben soll. Inwiefern sind Negation, Konjunktion und Disjunktion also nicht "Teil der modellierten Welt".

bzw, kann sie nicht sinnvoll das Interface Praedikat implementieren.

Inwiefern kann eine Negation (genauer: ein negierter Ausdruck) nicht die vier geforderten Methoden implementieren? Ein negierter Ausdruck kann doch nochmals negiert werden und er kann auch mit einem anderen Ausdruck konjunktiv oder disjunktiv verknüpft werden.

Das heißt, dort würde ich auf die Java Funktion zugreifen:

Das sollst du aber nicht. Ich finde das vorgegebene Interface wie schon erwähnt auch nicht optimal, aber es ist nun mal die Aufgabe. Wenn die Implementierung einer Negation, Konjunktion und Disjunktion zur Aufgabe zählen, finde ich es fragwürdig, als Antwort auf Java's boolesche Operatoren zu verweisen.

Letztlich ist es "defective by design" sowas in Java zu implementieren.

Mit Hibernate habe ich ja schon ein populäres Beispiel genannt, wo man etwas sehr Ähnliches macht.
 
Zweipunktnull schrieb:
Inwiefern kann eine Negation (genauer: ein negierter Ausdruck) nicht die vier geforderten Methoden implementieren? Ein negierter Ausdruck kann doch nochmals negiert werden und er kann auch mit einem anderen Ausdruck konjunktiv oder disjunktiv verknüpft werden.

nein, denn das:

!v(kandidat)

wäre nämlich in deiner Logik möglich:

NichtPraedikat.oder(AnderesPraedikat).istErfuellt(kandidat);

und ist kein gültiger logischer Ausdruck.

Meiner Meinung nach macht es keinen Sinn, dass ein Prädikat eine andere Methode als "boolean istErfuellt(Object kandidat);" enthält, und halt noch die überschriebene toString()-Methode. Aber ich habe auch irgendwie das Gefühl, dass ich nicht ganz verstehe, wass der Designer des Interfaces vorhatte.
 
Zuletzt bearbeitet:
Um es kurz zu machen: Hä? Was willst du damit sagen? Um den Ausdruck !v(string) zu konstruieren, müsstest du doch die not()-Methode auf dem Ausdruck v(string) aufrufen. Das ist aber keine gültige Disjunktion. In diesem Fall ist also schon die Disjunktion das Kaputte. Generell kannst du selbstverständlich jedes gültige negierte Prädikat nochmals negieren und beliebig mit anderen Prädikaten verknüpfen.

Ergänzung zu deiner Ergänzung:

Vorab drei kurze Definitionen der Begriffe Negation, Konjunktion und Disjunktion, da ich vermute, dass du da evtl. etwas durcheinander bringen könntest. Eine Negation ist ein Ausdruck bestehend aus einem Negationsoperator (Negator) und einem Operanden. Ebenso ist eine Konjunktion ein Ausdruck bestehend aus n Konjunktionsoperatoren (Konjunktoren) und n+1 Operanden (Konjunkten). Analog dazu ist eine Disjunktion ein Ausdruck bestehend aus n Disjunktionsoperatoren (Disjunktoren) und n+1 Operanden (Disjunkten). Der Einfachheit halber kann n auch auf 1 festgelegt werden.

Ich vermute, du hältst das NotPredicate für den Negator. Das NotPredicate ist aber ein negiertes Prädikat, eine Negation. Wie ich schon mehrfach erwähnt habe, ergibt ein NotPredicate alleine keinen Sinn, sondern es bezieht sich stets auf ein anderes Prädikat. Die auf einem NotPredicate-Objekt aufgerufene toString()-Methode gibt also niemals ein einzelnes Ausrufezeichen zurück, sondern stets "!"+operand.toString(). Somit ist der Ausdruck !v(string) auch "nach meiner Logik" nicht konstruierbar.
 
Zuletzt bearbeitet:
Zurück
Oben