# Java - Fachklassen sind überbestimmt



## saladass (6. Dez 2020)

Hallo,

ich muss im Zuge meines Studiums ein Programm schreiben, dass ich später an einer Grafischen Oberfläche anknüpfe.

Mit dem Stand bin ich jetzt so das ich alle Fachklassen, technisch richtig programmiert habe. Mein Prof allerdings beim Testat meinte, dass ich einen Logik Fehler bei der Umsetzung hatte oder immer noch habe und meinte "Mein Java-Programm sei überbestimmt".

Jetzt ist es so, dass entweder ich ihn nicht verstanden habe oder er nicht ganz nachvollziehen konnte wie ich eigentlich vorhabe das Programm umzusetzen.
Ich kann mir darauf kein Reim bilden und in seinem Skript, behandelt er diese Aussage nicht, obwohl diese seiner Erfahrung/Meinung nach, zu 90% bei jedem in seinem Kurs vorkommt.

In einer Fachklasse hab ich sechs Attribute mit unterschiedlicher Wertigkeit, sechs einzelne Variablen die unabhängig aus 8 Operations-Formeln errechnet werden können.

In meinem ersten Konstruktor hab ich das auch gut umgesetzt, nach ihm. Jetzt sieht er aber das Problem, dass wenn er in der Oberfläche eine Methode aufrufe, eine Inkonsistenz herrscht was Ergebnis Werte anbelangt.

Allerdings hab ich das so mir etwas vorgestellt, das ich einfach aus der Oberfläche mir die zugehörige Formel aussuche nach der ich meine gesuchten mit gegebenen Werten rechnen werde, und mir dazu dann einfach die passende Methode aufrufe, die mir auch immer die zugehörigen Set-Methoden aufrufe, die aus der Parameterliste hervorgeht. Um die gegeben Werte immer wieder neu zu setzen. Und ich bin auch nicht in der Überlegung abgeleitete Atribute anzulegen, da alle Formeln unabhängig arbeitende Operationen (inkl. Set-Methiden) sind.

Sein Problem wäre aber, das nach dieser Methode, er sich nicht mehr auf den Standardkonstruktor noch auf den Klassenkonstruktor verlassen kann.

Aber letztlich würde mir in der Oberfläche, erst gar nicht auffallen, was sein wirkliches Problem wäre, weil ich eh nicht drüber gestoßen wäre, noch den Klassenekonstruktur aufrufe, sagt er.

Vielleicht hat jemand die erfahrung, mir das etwas näher zu bringen, was genau er sich jetzt vorstellt oder was er von mir will. Weil ich es wirklich verstehen will, cheks aber halt nur leider nicht.

MfG


----------



## kneitzel (6. Dez 2020)

irgendwie fällt es mir gerade schwer, deine Aussage nachzuvollziehen.

Wenn ich es richtig verstanden habe, dann bekommst du eine Inkonsistenz, weil Werte voneinander abhängig sind aber du diese auch einzelne setzen kannst?

Also Beispiel: Du hast ein Fahrzeug. Das hat eine Geschwindigkeit und einen Verbrauch. Der Verbrauch ist natürlich abhängig von der Geschwindigkeit.

Wenn du also nun eine Instanz hast und setGeschwindigkeit aufrufst und sich der Verbrauch nicht anpasst, dann hast du einen Zustand, der nicht valide ist.

Habe ich diese Problematik richtig verstanden?


----------



## saladass (6. Dez 2020)

Dass könnte irgendwie so hinkommen, glaub ich. Bin mir aber nicht sicher, weil ich selber bei dem Thema "überbestimmt" auf dem Schlauch stehe..

Weil man könnte ja auch einfach reverse engineering betreiben und zurückrechnen. Das man mit der größe des Verbrauchs zurück rechnet wie schnell das Fahrzeug gefahren ist. 

Eine Formel lässt sich ja umformen wie man will.

Zum Beispiel U=R*I
Dan gibt es eine Operation: U=R*I, dan eine R=U/I, und dann eine I=U/R

Wäre das doch auch noch legitim?


----------



## kneitzel (6. Dez 2020)

Also generell musst Du auf einen konsistenten Zustand achten. Und dann kann es - um Dein Widerstand-Thema aufzugreifen - bedeuten, dass man einfach andere Werte mit ändert. Also da der Widerstand fix ist, dann würde sich bei einer Veränderung der Spannung auch der Strom ändern.

Generell muss man aber etwas aufpassen: Es ist unschön, wenn ein Setter für einen Wert noch weitere Konsequenzen hat, die man nicht erwartet. Bei Settern ist es eher unüblich, andere Werte direkt mit zu ändern...

Nehmen wir das Auto als Beispiel: Da werden gewisse Dinge nicht direkt änderbar gemacht. Ich kann die Geschwindigkeit ablesen, ok. Aber ich kann die Geschwindigkeit nicht setzen! Ich habe andere Möglichkeiten: Bremsen, Beschleunigen, Gegenstände verladen (ändert dann indirekt ja den Verbrauch und so ...) ... Somit bietet mir eine Klasse ein Verhalten an und das nutze ich.

Evtl. ist das etwas, was Dein Prof sehen möchte. Wichtig ist: Du willst in der Regel keine Inkonsistente Zustände - auch nicht temporär (so nach dem Motto: Das richte ich noch, ehe ich es anzeige).


----------



## mrBrown (6. Dez 2020)

Ich weiß nicht, was dein Prof mit "überbestimmt" meint, aber es könnte in diese Richtung gehen:

Deine Klasse hat U, R, und I als Attribute. Wenn du für diese jetzt "dumme" Setter bereit stellst, kommt es zu Inkonsistenzen, zb wenn man U einfach ändert, ohne die anderen anzupassen.
Kann man narütlich einfach lösen, indem man auch einen der anderen Werte anpasst – aber dann hat man das Problem, welchen der Werte man anpasst, man hat ja zwei Möglichkeiten, und möglicherweise ändert man dann grad die, die der Benutzer gar nicht geändert haben wollte.


Ich würde allerdings einfach den Prof fragen, wenn du nicht verstehst was er meinst, wir rätseln hier nur rum und haben möglicherweise eine völlig andere Sicht als der


----------



## saladass (6. Dez 2020)

Ursprünglich hab ich glaub auch indirekt drauf geachtet. Also ich kann jetzt nicht mein Projekt hier posten, aber ich hier mal an einem kleinen Beispielprogramm vom URI, hab ich das so gelöst:
[CODE lang="java" title="URI bsp."]public class URI {
    private double U;
    private double R;
    private double I;

    /**
    * Klassenlonstruktor
    */
    public URI(double new_U, double new_R, double new_I)throws Exception {
        set_U(new_U);
        set_R(new_R);
        set_I(new_I);
    }
    /**
    * Standartkonstruktor
    */
    public URI() {
        U = 6;
        R = 3;
        I = 2;
    }
//------Operationen------//
    pulic double calc_U(double new_R, double new_I) throws Exception {
        set_R(new_R);
        set_I(new_I);

        double result_U = R*I;
        this.U = result_U;
        System.out.println("Spannung: U = " +result_U+" V");
        return U;
    }

    pulic double calc_R(double new_U, double new_I) throws Exception {
        set_U(new_U);
        set_I(new_I);

        double result_R = U/I;
        this.R = result_R;
        System.out.println("Spannung: R = " +result_U+" Ω");
        return R;
    }

    pulic double calc_I(double new_U, double new_R) throws Exception {
        set_U(new_U);
        set_R(new_R);

        double result_I = U/R;
        this.I = result_I;
        System.out.println("Spannung: I = " +result_I+" A");
        return UI
    }

//------Getters & Setters------//

    /**
     * Get-Methode: U
     * <p>
     * Lädt Attributwert U und gibt diesen zurück.
     * <p>
     * @return double U
     */
    public double get_U() {
        return U;
    }

    /**
     * Set-Methode: U
     *<p>
     * Überschreibt alten mit neuen Attributwert U
     * <p>
     * @param double new_U
     */
    public void set_U(double new_U) throws Exception {
        this.U = new_U;
    }

        /**
     * Get-Methode: R
     * <p>
     * Lädt Attributwert R und gibt diesen zurück.
     * <p>
     * @return double R
     */
    public double get_R() {
        return R;
    }

    /**
     * Set-Methode: R
     *<p>
     * Überschreibt alten mit neuen Attributwert R
     * <p>
     * @param double new_R
     * @throws Exception - wird geworfen, wenn R kleiner-gleich ≤ 0 bzw. negativ ist
     */
    public void set_R(double new_R) throws Exception {
        if(new_R <=0) {
            throw new Exception("R muss positiv sein.");
        }
        this.R = new_R;
    }

        /**
     * Get-Methode: I
     * <p>
     * Lädt Attributwert I und gibt diesen zurück.
     * <p>
     * @return double I
     */
    public double get_I() {
        return IU;
    }

    /**
     * Set-Methode: I
     *<p>
     * Überschreibt alten mit neuen Attributwert I
     * <p>
     * @param double new_I
     * @throws Exception - wird geworfen, wenn I kleiner-gleich ≤ 0 bzw. negativ ist
     */
    public void set_I(double new_I) throws Exception {
        if(new_I <=0) {
            throw new Exception("I muss positiv sein.");
        }
        this.I = new_I;
    }
}[/CODE]

Aber mir scheint es das ich ein Logik bzw. denk Fehler bei der Umsetzung habe. MIt dem Prof hab ich versucht das zu besprechen, aber ist jetzt auch nicht leicht zu verstehen auf ersten anhieb.

Aber so hab ich diese Prozedur, mal als beispiel Programm. Mir erschliest sich nur nicht, wo ich den denkfehler habe.


----------



## mrBrown (6. Dez 2020)

Es ist damit eben nicht gelöst, sondern damit ist das Problem erst vorhanden 


```
URI uri = new URI(1, 1, 1); 
// Hier gilt U=R*I (1 = 1*1)

uri.set_U(42); 
// Hier gilt U=R*I *nicht* (42 != 1*1)
```


----------



## saladass (6. Dez 2020)

Thx, erstmal für die Antworten.
Also, um das Problem zu beseitigen brauch ich doch eine Dynamische Variable die immer mit zieht? Also doch ein Abgeleitetes Atribut?
Oder müsste ich im Klassenkonstruktor, zum Beispiel statt set_U, einfach calc_U(new_U) aufrufen.
Hab ich dass Problem dan auch umgangen, wenn ich auf einzelne Methoden zurückgreife?

Das wäre jetzt so die zwei Lösungsmöglichkeiten die sich mir erschließen auf anhieb.


----------



## mrBrown (6. Dez 2020)

Das ist eher ein fachliches, und kein technisches Problem – eine allgemeingültige Antwort darauf zu geben ist nicht wirklich möglich.

Bei dem URI-Beispiel könnte man z.B. festlegen, dass:

man Werte nicht einzeln ändern kann
man immer angibt, welcher Wert geändert werden soll (z.B., U soll geändert werden, R soll angepasst werden: `setU(17, R)`)
intern festgelegt ist, welche Werte sich wann ändert (zB, ändert man U wird I angepasst, ändert man I wird R angepasst, ändert man R wird U angepasst...)
man lässt gar keine Anpassungen zu, Anpassungen laufen immer über einen "Builder" und die alten Werte werden nur als "Vorlage" genutzt und beim erstellen des Objekte wird dann geprüft, ob es valide ist
es werden nur zB U und I gespeichert und R immer nur berechnet, R ist damit aber auch gar nicht "manuell" anpassbar


----------



## kneitzel (6. Dez 2020)

Das ist so pauschal nicht zu beantworten.

Fakt ist, das so ein Setter ein inkonsistenten Zustand schafft. Das darf nicht sein. Also muss der Setter so weg.

Die Frage ist doch, was du da überhaupt machen willst. Du musst ein sauberes Verhalten definieren. Das kann sein, dass Du die Spannung änderst und der Widerstand konstant ist. Dann würde sich der Strom ändern.
Das aber dann bitte nicht als Setter....

Und das Problem ist doch schon das Beispiel selbst. Klasse URI? Was ist ein URI? Also ganz offensichtlich existiert ein Problem beim Design.

Auch an einer Spule hast Du Strom, Spannung und Widerstand ... aber nicht so eine Regel ... da kommt ja auch noch die Frequenz mit rein bei Wechselstrom und so ...


----------



## mrBrown (6. Dez 2020)

kneitzel hat gesagt.:


> Und das Problem ist doch schon das Beispiel selbst. Klasse URI? Was ist ein URI? Also ganz offensichtlich existiert ein Problem beim Design.
> 
> Auch an einer Spule hast Du Strom, Spannung und Widerstand ... aber nicht so eine Regel ... da kommt ja auch noch die Frequenz mit rein bei Wechselstrom und so ...


Das ist allerdings wohl meine Schuld 😅


----------



## saladass (6. Dez 2020)

Also das mit URI war nur auf der schnelle ein Beispiel um irgendwie mein Problem vom Verständnis her näher zu bringen.

Also hauptsächlich gehts mir dann nur darum im Programm, das ich dann einfach an der Oberfläche drei Felder habe. Jedes Feld vertritt eine zugehörige aufgerufene Berechnungsmethode, und je nachdem welche Formel ich aufrufe, z.B. das Feld für R=U/I, soll mir die Methode `calc_R()` aufrufen. Diese soll mir dann einfach nochmal ein Fenster zum Rechner öffnen, in dem die abgefragten gegeben Werte eingebe und mir dann das Ergebnis als gesuchten Wert ausgibt.

Ist das irgendwie so verständlich? So Basic mäßig für mich halt. Versuche das GUI mäßig, die ersten Erfahrungen erstmals aufzubauen.

Wäre, dass dann mit meinem ursprünglichen Umsetzungs-Design noch vertretbar, das so umzusetzen? Wenn schon solche inkonsistenzen drinn sind, ist es doch schwierig. Die Erfahrung bau ich erstmals grade auf.


----------



## kneitzel (6. Dez 2020)

Ja, das war nur ein Beispiel und an dem Beispiel haben wir versucht, Dir die Problematik mit den Settern zu erläutern.

Du musst verstehen, was an dem Design schlecht ist bzw. was da bemängelt wurde. Das war du da beschreibst ist nur ein Verhalten des Programmes und besagt nichts zu dem Design der Klassen darunter.

Generell sind so Inkonsistenzen zu vermeiden. Generell ist es ein Unding, immer nur über Setter zu gehen statt ein sauberes Verhalten zu definieren.

Neben dem sauberen definieren von Verhalten könnte man auch unveränderliche Instanzen einführen. Das ist aber nur sinnvoll, wenn die Klassen sowas wie einen Wert darstellen. Dann gibt es da keine Referenzen drauf.

Bedeutet: Du hast dann eine Instanz URI. Aber wenn dann Werte in der Oberfläche verändert werden, dann wird eine neue Instanz URI erzeugt und die Werte daraus angezeigt. URI hat dann keine Setter mehr sonder nur Getter. Und zum erstellen neuer Instanzen gibt es dann nur Konstuktoren oder statische Methoden, die dann neue Instanzen erzeugen und zurück geben.

Da wir keine Details kennen können wir aber keine konkreteren Lösungsvorschläge machen. Anhand der Beispiele können wir nur aufzeigen, wo wir die Probleme sehen.


----------



## saladass (6. Dez 2020)

Alles gut, ich bin ja auch hier um zu lernen.

Also das Problem ist das eine inkonsistenz herscht, welche von den Settern ausgeht.

Könnte ich statt Setter zu benutzen, die mir einen inkonsistenten Zustand erzeugen, dan einfach eine erneute Benutzereingabe abverlangen. Die dann zusätzlich an Bediengungen geknüpft sind?

Zum Beispiel so:


```
Scanner eingabewert = new Scanner(System.in);
System.out.println("Eingabewert U [V]: ");
U = eingabewert.nextDouble();
```


----------



## fhoffmann (6. Dez 2020)

saladass hat gesagt.:


> Könnte ich statt Setter zu benutzen, die mir einen inkonsistenten Zustand erzeugen, dan einfach eine erneute Benutzereingabe abverlangen. Die dann zusätzlich an Bediengungen geknüpft sind?


Nein,

das solltest du nicht tun!

Deine Klasse URI (Widerstand) muss selber dafür sorgen, dass ihr Zustand immer konsistent ist.
Das darf nicht von der Eingabe eines Users abhängig sein.

In 10 Jahren möchtest du die Klasse URI erneut verwenden und verlässt dich auf sie (oder ein Kollege möchte sie verwenden und verlässt sich auf dich). Dass sie einmal so geplant war, dass bei der Eingabe der Werte bestimmte Kontrollen stattfinden, weiß dann niemand mehr. Deshalb muss die Klasse das selber regeln.

In deinem ursprünglichen Problem scheint es auch so zu sein, dass du erwartest, dass bestimmte Methoden in einer bestimmten Reihenfolge aufgerufen werden. Das kannst du natürlich dadurch sicherstellen,  indem du die Oberfläche entspechend programmierst. Aber auch das solltest du nicht tun.


----------



## saladass (6. Dez 2020)

Gibt es den gewisse spezielle Programme oder Hilfen, die auf Inkonsistenzen bzw. Überbestimmtheit hinweisen. Ein Add-On in eclipse vielleicht.

So oder so müsste ich ein Fachklassentest jetzt machen. Wäre BlueJ eine herangehenweise, über die Objektleiste. Hab sonst alles in eclipse programmiert.
Wie ist den die klügste Weise ein solchen Plausibilitätstest zu machen? Geht auch Modultest?


----------



## fhoffmann (6. Dez 2020)

saladass hat gesagt.:


> Gibt es den gewisse spezielle Programme oder Hilfen, die auf Inkonsistenzen bzw. Überbestimmtheit hinweisen.


Nachdenken!


----------



## kneitzel (6. Dez 2020)

Ein Tool kann die fachliche Korrektheit doch nicht erkennen. Das kannst Du nur in eigenen Tests überprüfen.

Eclipse, bluej oder irgend ein anderes Tool kann doch nicht wissen, dass Du z.B. einen Widerstand programmierst und dass immer gelten muss: U = I * R.

Das sind Logiken, die Du als Entwickler ja schreibst und Du bist damit auch für die Tests verantwortlich. Aber so Du die Tests entsprechend geschrieben hast, dann kann die fachliche Korrektheit durch den Unit Test Runner geprüft werden. So Du aber bei den Tests es nicht abprüfst, dann ist es natürlich wieder Dein Versäumnis.


----------



## saladass (6. Dez 2020)

Okey, danke nochmals für das ganze Feedback. Ich werd mich mal mit dem Thema Inkonsistenz, weiterhin befassen. Lernt man immerhin nicht alles an einem Tag. Warscheinlich ist das genau der Punkt wo es in die Fachklassentest schon geht, nur hab ich das selber nicht gemerkt. 😅

Aus Wikipedia konnte ich nur das für Informatik zitieren, weil das Wort konsistenz gefallen ist.


> In der Informatik bedeutet Inkonsistenz von Daten Widersprüchlichkeit zwischen den Daten. So können in einer Datenbank beispielsweise Verknüpfungen zwischen Tabelleneinträgen nicht mehr eindeutig sein, weil der Verbindungsschlüssel auf keinen oder auf mehrere Einträge in einer anderen Datenbanktabelle verweist. Gängige Datenbank-Management-Systeme prüfen meist selbst auf Inkonsistenzen und weisen mit Fehlermeldungen darauf hin.
> - Quelle: https://de.wikipedia.org/wiki/Inkonsistenz


Hat mir ein bisschen geholfen. 
Aber bin jederzeit immer wieder gerne für weitere Feedback und Problemansätze offen.


----------



## mihe7 (7. Dez 2020)

saladass hat gesagt.:


> Mir erschliest sich nur nicht, wo ich den denkfehler habe.


Dem Programm nach zu urteilen würde ich sagen, dass Du nicht in Objekten sondern in Funktionen/Prozeduren denkst. Um mal bei dem Beispiel zu bleiben, würde man z. B. einen elektrischen Widerstand modellieren, der einen Wert (in Ohm) annehmen kann:

```
class Widerstand {
    private int ohm;

    public Widerstand(int ohm) {
        this.ohm = ohm;
    }
}
```

Den Widerstand kann ich nun fragen: hey, Widerstand, wie viel Spannung fällt an Dir ab, wenn ich 2 Ampere durch Dich durchjage?

```
public double berechneSpannungsabfall(double ampere) {
        return ohm * ampere;
    }
```
Oder ich frage ihn, wie viel Strom er zieht, wenn ich eine bestimmte Spannung anlege:

```
public double berechneStrom(double spannung) {
        return spannung / ohm;
    }
```

Den Widerstand könnte ich ggf. sogar ändern:

```
public void aendereWert(int ohm) {
        this.ohm = ohm;
    }
```

Der Wert des Widerstands und die Ergebnisse der Berechnungen sind damit immer konsistent.


----------

