Konstruktorverkettung

Wie baut man in den folgenden Datentyp Aufgabe eine Konstruktorverkettung innerhalb der
Klasse so auf, dass die Tests für die Gültigkeit der Nummer nr nur noch an einer Stelle
stehen (müssen).
Danke!

public class Aufgabe
{

    private int nummer;
    private String name;

    public Aufgabe(int nr)
    {
         //Verschiedene Tests für Gültigkeit der Nummer nr nummer = nr;
    }
    public Aufgabe(String text, int nr)
    {
        name = text;
        // Verschiedene Tests für Gültigkeit der Nummer nr
        nummer = nr;
    }

}```

Ein Tipp, der nicht aus dem Posten des Codes besteht, fällt mir gerade nicht ein

    public Aufgabe(String text, int nr)
    {
        this(nr);
        name = text;
    }

Oder du kannst eine Methode machen, die dir zurückgibt, ob die Zahl gültig ist, also zum beispiel:
public boolean isValid(int nr){//...}
Die kannst du dann in beiden Merhoden aufrufen. Wenn für eine Gültige Zahl immer das selbe passieren soll kannst du das sogar auch noch mit in die methode übernehmen. Die kann man dann unter unmständen auch benutzen, um den Wert der Variablen später nochmal zu verändern

[QUOTE=Marco13]Ein Tipp, der nicht aus dem Posten des Codes besteht, fällt mir gerade nicht ein

    public Aufgabe(String text, int nr)
    {
        this(nr);
        name = text;
    }
```[/QUOTE]Nanana... wollen wir nicht lieber den Konstruktor mit den meisten Parametern am Ende der "Kette" haben?
```public class Aufgabe
{
 
    private int nummer;
    private String name;
 
    public Aufgabe(int nr)
    {
        this(null, nr); // wenn Text nicht null sein darf, Defaulttext übergeben.
    }
    public Aufgabe(String text, int nr)
    {
        name = text;
        // Verschiedene Tests für Gültigkeit der Nummer nr
        nummer = nr;
    }
 
}```

Natürlich. Aber solange nicht klar ist, ob im „letzten“ Konstruktor dann als erstes sowas steht oder stehen soll wie

if (text == null) throw Spratzl();

weiß man eben nicht, wie die unteren „Teleskopaufrufe“ aussehen müßten …

Das wäre doch unsinnig. Wofür wäre denn dann der andere Konstruktor? Offensichtlich ist es doch valide die Property text mit null zu belegen. :slight_smile:

[QUOTE=Sym]Das wäre doch unsinnig. Wofür wäre denn dann der andere Konstruktor? Offensichtlich ist es doch valide die Property text mit null zu belegen. :)[/QUOTE]Das ist nicht gesagt. Nur in diesem Beispiel ist das so. Beachte dazu Marcos Einwand oder den Kommentar in Zeile 9 meines Codes (welchen Marco evtl. übersehen hat). Normalerweise wird der Member “name” ja auch nicht einfach so im Konstruktor zugewiesen sondern per Setter gesetzt.

    {
     
        private int nummer;
        private String name;
     
        public Aufgabe(int nr)
        {
            this(null, nr); // wenn Text nicht null sein darf, Defaulttext übergeben.
        }
        public Aufgabe(String text, int nr)
        {
            setName(text);
            setNumber(nr);
        }
        private void setName(String name) {
            // Parameter pruefen
            this.name = name;
        }
        private void setNumber(int nummer) {
            // Parameter pruefen
            this.nummer = nummer;
        }
    }```Im Verlauf der Entwicklung kann man sich so immernoch überlegen, ob man die Setter öffentlich macht.

Ja, sonst gibt es halt einen Defaulttext. Trotzdem wäre eine andere Verkettung “unnormal” bzw nicht notwendig. Und das ist sie meiner Meinung nach nie.

[QUOTE=Sym]Ja, sonst gibt es halt einen Defaulttext. Trotzdem wäre eine andere Verkettung „unnormal“ bzw nicht notwendig. Und das ist sie meiner Meinung nach nie.[/QUOTE]Ach so… ja… Mein Reden. :wink:

Hab’ ich tatsächlich :o

Den per Setter zu setzen wäre eigentlich „schöner“, das stimmt. Leider hat das zwei Pferdefüße:

  • Das macht niemand :wink:
  • Die Variablen können nicht mehr final sein

Für letzteres gibt es in bestimmten Zusammenhängen (Guava z.B.) solche Methoden wie

private static <T> T checkNonNull(T t, String name) 
{
    if (t == null) throw new NullPointerException(name+" may not be null");
    return t;
}

class Foo
{
    private final String string;
    public Foo(String string)
    {
        this.string = checkNonNull(string, "string");
    }
}

Naja … muss man sich halt überlegen…

[quote=Spacerat]Normalerweise wird der Member “name” ja auch nicht einfach so im Konstruktor zugewiesen sondern per Setter gesetzt.[/quote]Wieso ist das setzrn des Namens per Setter “normal”?

bye
TT

“Normal” ist zwar sehr schwammig, aber der Vorteil hier wäre ja offensichtlich: Man muss die Gültigkeitsprüfung nur an EINER Stelle schreiben.

Warum sollte ein Privat-Setter Sinn haben, wenn dort nur das Attribute gesetzt wird?

Genau aus dem gleichen Grund, aus dem ein public setter Sinn macht: Vielleicht soll dort irgendeine Prüfung stattfinden (nicht-null, String nicht leer…) - es kann nicht schaden, das auch im privaten Umfeld sicherzustellen (auch wenn es keiner macht - deswegen hatte ich schonmal die Möglichkeit in den Raum gestellt, dass ein access-modifier, der stärker ist als ‘private’, durchaus Sinn machen könnte…)

Dann sollte aus dem Namen der Methode auch klar werden, was sie tut. Ein Setter sollte (gerade wenn public) eigentlich nur das Feld setzen (ja, ich weiß, es gibt auch ab und zu die Ausnahme der Regel).

Wenn es nur um Feldzuweisung geht, ist ein private-Setter unnötig. Wenn er mehr macht, sollte er einen entsprechenden Namen erhalten, wie z.B. initName(…).

[QUOTE=Sym]Dann sollte aus dem Namen der Methode auch klar werden, was sie tut. Ein Setter sollte (gerade wenn public) eigentlich nur das Feld setzen (ja, ich weiß, es gibt auch ab und zu die Ausnahme der Regel).

Wenn es nur um Feldzuweisung geht, ist ein private-Setter unnötig. Wenn er mehr macht, sollte er einen entsprechenden Namen erhalten, wie z.B. initName(…).[/QUOTE]Also ich finde “setSonstwas()” als Init-Methode passend genug, wobei ich zustimme, dass “normal” doch sehr schwammig ist. Aber es ist durchaus praktikabel, selbst wenn man finale Member damit nicht gesetzt bekommt. Aber da bleibt ja als Trost, dass man private Member nicht mal final machen muss.

Warum sollte ich ein Member, welches eigentlich final sein könnte, nicht final machen?

[QUOTE=Sym]Warum sollte ich ein Member, welches eigentlich final sein könnte, nicht final machen?[/QUOTE]Weil du dir im Verlauf der Projektentwicklung nie über dieses final im klaren sein kannst. Ok… zumindest weisst du bei finalen Membern die Initialisierung stattfindet aber mach da im weiteren Verlauf mal einen nicht finalen Member draus. Ich finde private ist final genug.

„Müssen“ tut man als Entwickler ziemlich wenig (was nebenbei gesagt einer anti-autoritären Erziehung sehr entgegen kommt :D). Die Frage, welche Existenzberechtigung ein setter/getter-Paar denn (noch) haben kann, wenn darin wirklich immer und für alle Zeit NUR gesettet und gegettet werden soll, ist zwar berechtigt, aber ich sehe letzteres nicht so eng. Es geht ja auch um die Bedeutung - um das, was damit modelliert werden soll. Natürlich muss man immer abwägen und überlegen, aber ein „getter“ kann z.B. IMHO auch was „lazy“ initialisieren…

List list = null;
List getList() 
{ 
    if (list==null) list = new List(); 
    return list;
}

(Ich weiß, in Clean Code steht da dann ‚getOrInitializeList‘ - grauslig…). Und genauso kann und sollte ein setter IMHO Argumente prüfen (speziell natürlich public setters).

Eine der Rechtfertigungen für setter ist ja, dass man mehr Freiheiten bezüglich der internen(!) Modellierung hat. Dass man eben noch weitere Aktionen ausführen kann, wenn sie aufgerufen werden (angefangen beim Logging, aber das ist natürlich nur das erst-plakativste Beispiel dafür).

Nochmal: Man muss immer abwägen. Wer z.B. einen getter aufruft, erwartet, dass die Ausführung der Methode „einigermaßen schnell“ geht, und nicht erst irgendwas kompliziertes berechnet wird. Aber wenn ich die Wahl hätte, bei einem setName(null) sofort eine IAE zu bekommen, oder später an ganz anderer Stelle eine NPE zu bekommen, und erstmal debuggen zu dürfen, wann und wo denn welche Variable dort ‚null‘ ist und wo der entsprechende Setter mit dem falschen Argument aufgerufen wurde, wäre mir ersteres doch lieber… :wink:

Oh, das sehe ich komplett anders. :slight_smile: Was final ist, sollte es auch sein. Das ist KISS. Wenn es geändert werden muss, dann passe ich es dann an. Und final dokumentiert den Code. Das wäre für mich „clean“. Ein private setter für ein „eigentlich final Feld“ ist für mich sowas von unschön/unnötig. Wenn so etwas durch mein Review geht, wird das behoben. :wink:

*** Edit ***

In einem Setter eine IAE zu werfen ist auch vollkommen legitim. Und eine Liste in einem Getter zu Initialisieren ist auch ok. Aber Berechnungen sollten im Setter/Getter nie passieren. Dafür sollte es spezielle Methoden geben. Leider gibt es in Java nicht wie in C# das Property-Pattern.

Und es gibt schon „müssen“ als Entwickler. Zumindest wenn man in großen Teams wartbaren Code entwickeln möchte. :slight_smile: