Variablen durch funktionale Konstrukte vermeiden

In letzter Zeit habe ich häufiger ein bestimmtes Konstrukt gesehen. Ich dachte erst, das wäre eine „übliche Praxis in JavaScript“, aber nun sehe ich das gleiche in Java - es ist also wohl eher eine „übliche Praxis des Autors“ :wink:

Das Muster zielt offenbar grob darauf ab, … Variablen zu vermeiden (!?), und zwar durch Funktionale Konstrukte:

void init(int parameter ) {

    Supplier<Value> supplier = () -> {
        if (parameter > 10) {
            return Value(123);
        }
        return Value(456);
    };
    final Value value = supplier.get();
    use(value);
}

Das ganze ist also analog zu

void init(int parameter ) {
    Value value = null;
    if (parameter > 10) value = new Value(123);
    else value = new Value(456);
    use(value);
}

oder, für die null-Hasser sowas wie

void init(int parameter ) {
    Value value = getValue(parameter);
    use(value);
}
private static Value getValue(int parameter) {
    if (parameter > 10) {
        return Value(123);
    }
    return Value(456);
}

(man könnte also sagen, dass einfach statt einer privaten Methode ein lokaler Supplier verwendet wird).

Ist das „Functional Programming Gone Wild“, oder übersehe ich etwas?

Ich sehe keinen vernünftigen Grund, sowas zu verwenden. Variablen „spart“ man damit jedenfalls nicht.

Ein Supplier ist sozusagen „Laziness für Arme“, aber diese Aufgabe erfüllt er hier nicht.

Im größeren Rahmen könnte ich mir zwar vorstellen, diesen Part (recht frei nach Clean Code) in eine eigene Funktion auszulagern um den Code lesbarer zu halten, ich kann hier aber auch nichts ‘funktionales’ oder auch sinnvolles daran erkennen.

In der JavaScript-Welt hatte ich das darauf geschoben, dass var ja schon ziemlicher Murx ist, und man wenn möglich const verwenden sollte. Aber erstens könnte man das Problem der Zuweisung dann ja mit let lösen, und zweitens tritt dieses Problem bei Java ja nicht auf.

Ein (vage: ) „Funktional gepägter (persönlicher) Stil“ erschien mir wie eine andere mögliche Erklärung. Funktional ist ja gerade in, lambdas überall… Ich sehe die Vorteile von („echter“) Funktionaler Programmierung, aber … wenn solche Dinger dann auch noch verschachtelt werden, sieht das dann halt schon nach einem Muster aus, das pauschal und ohne Abwägung von Vor- und Nachteilen verwendet wird (a la „Funktional-ität um der Funktional-ität Willen“, oder der Klassische Schweizer Offiziershammer, der auf jeden Nagel passt :confused: )

Variablen vermeiden würde ich nicht sagen. Im Prinzip könnte man den hier vorgestellten Code einfach als Oneliner anschreiben

final Value value = parameter > 10 ? new Value(123): new Value(456);

Damit würde wirklich Variablen sparen, nämlich die supplier Variable. Das vorgestellte Muster macht meiner Meinung nach nur Sinn, wenn man etwas komplexeres initialisieren will, aber keine Unterklasse bilden will (weiß Gott warum) oder kann (final). Sonst sehe ich nicht wirklich einen Sinn dahinter.

Wobei man bei JavaScript die Möglichkeit hat, das Muster ohne zusätzliche Suppliervariable zu definieren, in etwa so:

const value = function() {
    if (parameter > 10) {
        return Value(123);
    }
    return Value(456);
}();

Sieht IMHO ein wenig eleganter aus als in Java.

Offtopic

In einem deiner Threads hast du dich ja bereits groß aufgeregt, dass man Properties bei Objekten einfach hinzufügen oder verändern kann ohne Probleme. Um Unbedachtsame daran zu hindern wäre Objekt.freeze vielleicht praktisch.

Wenn schon, denn schon:

 final Value value = new Value(parameter > 10 ? 123 : 456);

OT: Ein Beispiel, wo ich mich immer frage: WIESO???

if (cond) {
    return foo().baz(); 
} else {
    return bar().baz();
}

statt

return (cond ? foo() : bar()).baz();

Naja könnte man Gründe der Lesbar/Wartbarkeit anführen.
Wenn du da jetzt statt foo().baz() ein foo().baz().bar() machen willst musst du dein Konstrukt erstmal aufdröseln

Aber ja ich mache auch immer lieber letzteres :smiley:

Das sollte die IDE deines Vertrauens für dich erledigen.

Der Konsens scheint zu sein, dass es dafür keinen vernünftigen Grund zu geben scheint.

(Nur nochmal zusammengefasst, weil das jetzt (noch nicht ganz, aber langsam) in OT abdriften könnte)


Natürlich, der Ternäre ?:-Operator wäre die einfachste Alternative, aber da hat der Verantwortliche klar gesagt, dass er den „nicht mag“ (und ggf. früher oder später per Lint-Policy deaktivieren wird). Für sowas wie

boolean someCondition = (foo.bar() > 32 && baz.some.property < 123.0) ||
    (yesterday.value > today.value && !(something != somethingElse) || 
    !(cat instanceof Animal)) ? some.deeply.hidden.property && 
    some.even.more.deeply.hidden.property : false;

verstehe ich das, aber für ein

return result > 0.5 ? RED : GREEN;

eben nicht. Zu dumm, dass man „Programmierer-Vernunft“ nicht per Policy aktivieren kann :wink:


Diese „in-place-invocation“ von

const value = function() {
...
}();

mag „elegant“ sein, aber finde ich sehr heikel. Die beiden () sind schon sehr subtil, und ggf. ein paar Zeilen weiter unten. Und gerade wegen der Ungetyptheit ist das IMHO brandgefährlich…

const mayAccessTheDatabase = function() {
    if (password === "Secret") return true;
    printError();
    return false;
};

if (mayAccessDatabase) { 
    return secretData;
}

Dort fehlen nur die () … sehr subtil.

(Wobei… es gibt zwei Stellen, wo die () eingefügt werden könnten - das ist der Punkt, wo man einfach wieder mit Sprachbashing anfangen muss ;-))

Soweit ich weiß geht es bei diesem Pattern nicht um Eleganz, sondern darum, vernünftiges Scoping in JS zu simulieren. Wo in anderen Sprachen ein simpler Block ausreicht, um Variablen gegen Zugriff von „außen“ zu schützen, braucht man in JS stattdessen solche Konstrukte.