Funktionen miteinander verketten

Robertop

Bekanntes Mitglied
Hallo zusammen,

ich würde mir gern eine Klasse schreiben, mit der ich Funktionen "verketten" kann, um beim Programmieren natürlichere Ausdrücke verwenden zu können. Also ein einfaches Beispiel wäre sowas wie

Java:
stageObject.first("Hallo").second("Welt!");

Leider fehlt mir für sowas ein wenig das theoretische Wissen. Meine Idee bisher ist, dass ich eine Klasse mit einer inneren Klasse schreibe, die dann als Rückgabetyp der ersten Funktion ausgegeben wird, sodass man ab da die zweite Funktion benutzen kann:

Java:
public class Stage {
    
    private String string1 = "";

    private class SecondStage {
        private SecondStage() {}
        
        public void second(String string) {
            System.out.println(string1 + " " + string);
        }
    }
    
    public SecondStage first(String string) {
        string1 = string;
        return new SecondStage();
    }
}


Dazu habe ich jetzt diese Fragen:

- Ist das ein guter Weg, das zu lösen, oder gibt es da andere Vorgehensweisen? Wonach müsste ich bei google Suchen, um sowas zu finden? Bei meinen Versuchen finde ich immer nur alles mögliche zu Vererbung und Interfaces und so.

- Ist sichergestellt, dass der Inhalt von string1 beim Aufruf von second() immer richtig ist? Bei meinen Versuchen hat es immer funktioniert, aber im Prinzip erstelle ich ja in der first() Funktion ein neues Objekt von SecondStage(). Wird dieses neue Objekt wirklich in allen Lebenslagen mit demselben schon bestehenden Objekt von Stage zusammenhängen?

- Es soll natürlich am besten gar nicht möglich sein, ein alleinstehendes SecondStage Objekt zu bekommen. Am liebsten wäre mir sogar, wenn man sowas wie Object object = stage.first("Hallo"); gar nicht machen kann. Kann man das irgendwie verhinden?
 
Also wenn Du googlen willst, wie sowas aufgebaut werden könnte, dann wäre z.B. Builder Pattern etwas, das so in der Art aufgebaut ist. Aber auch bei den Streams findet man sowas (inc. Wechsel des Typs z.B. bei map) wobei da Google vermutlich mehr zur Nutzung von Streams auswerfen wird denn zu der Entwicklung der Streams und dessen Design selbst.

Die innere Klasse hat den klaren Bezug zur äußeren Klasse - das wird also funktionieren, aber generell ist da natürlich nichts geschützt. Ich kann first aufrufen und die SecondStage in eine Variable packen um dann ggf. erst noch andere Aufrufe zu machen. Also z.B. sowas:
Java:
Stage stage = new Stage();
Stage.SecondStage second1 = stage.first("Hallo");
Stage.SecondStage second2 = stage.first("Na");
second1.second("Welt");
second2.second("Du");

Durch den zweiten Aufruf wird string1 natürlich überschrieben.
 
- Ist das ein guter Weg, das zu lösen, oder gibt es da andere Vorgehensweisen?

Das kommt darauf an was genau du vorhast. Ich glaube was du vorhast ist eine Unterform vom Builder-Pattern, naemlich das Step-Builder Pattern. Bricht man sich zwar fast die Finger beim tippen, ist aber echt eine coole Sache fuer denjenigen der es danach benutzt.
Ist sichergestellt, dass der Inhalt von string1 beim Aufruf von second() immer richtig ist?

Definiere "richtig". Wenn du meinst "es ist genau dieser Wert welcher vorher gesetzt wurde", dann Ja, es ist immer richtig. Innere Klassen in Java, welche nicht statisch sind, halten sich eine Referenze auf die Instanz ihrer Eltern-Klasse. Also so um das etwas zu veranschaulichen:

Java:
public class Outer() {
    protected int theValue = 4;
    
    public Inner setTheValue(int theValue) {
        this.theValue = theValue;
        
        return new Inner();
    }
    
    protected class Inner {
        public void doSomething() {
            System.out.println(theValue);
        }
    }
}

Sieht in "Wahrheit" wenn es ausgefuehrt wird so aus:

Java:
public class Outer() {
    protected int theValue = 4;
    
    public Inner setTheValue(int theValue) {
        this.theValue = theValue;
        
        return new Inner(this);
    }
    
    protected class Inner {
        protected Outer outerInstance = null;
        
        public Inner(Outer outerInstance) {
            this.outerInstance = outerInstance;
        }
        
        public void doSomething() {
            System.out.println(outerInstance.theValue);
        }
    }
}

Also die Referenz auf diese Instanz der aeusseren Klasse ist eindeutig.

Es soll natürlich am besten gar nicht möglich sein, ein alleinstehendes SecondStage Objekt zu bekommen. Am liebsten wäre mir sogar, wenn man sowas wie Object object = stage.first("Hallo"); gar nicht machen kann. Kann man das irgendwie verhinden?

Nein. Was genau versuchst du da zu bezwecken damit?
 
Es soll natürlich am besten gar nicht möglich sein, ein alleinstehendes SecondStage Objekt zu bekommen. Am liebsten wäre mir sogar, wenn man sowas wie Object object = stage.first("Hallo"); gar nicht machen kann. Kann man das irgendwie verhinden?
Jein. Natürlich kannst Du nicht verhindern, dass der Aufrufer den Rückgabewert verwendet, allerdings kannst Du durch dessen Typ einschränken, wofür er verwendet werden kann.

Mal ein einfaches Beispiel:
Java:
public class Car {
    public static class Builder {
        private String brand;
        private String model;
        public Builder brand(String value) { this.brand = value; return this; }
        public Builder model(String value) { this.model = value; return this; }
        public Car build() {
            if (brand == null || model == null || brand.trim().isEmpty() || model.trim().isEmpty()) {
                throw new IllegalStateException();
            }
            return new Car(this);
        }
    }

    private final String brand;
    private final String model;

    private Car(Builder builder) {
        brand = builder.brand;
        model = builder.model;
    }
}
Du kannst also nicht verhindern, dass jemand
Java:
Car.Builder b = new Car.Builder().brand("ABC");
schreibt. Allerdings muss Dich das nicht interessieren, weil Du kein unvollständiges Car-Objekt erzeugen kannst. Ein
Java:
Car car = b.build();
führt hier zu einer IllegalStateException. Schreibst Du dagegen:
Java:
Car.Builder b = new Car.Builder().brand("ABC");
b.model("1"); // oder b = b.model("1");
Car car = b.build();
funktioniert das natürlich genauso wie
Java:
Car car = new Car.Builder().brand("ABC").model("1").build();
 
Evtl. hilfreich, da noch den Hinweis klar zu sagen:
Eben keine Werte in der äußeren Klasse haben.
a) Innere Klasse static halten -> Damit hast Du keine Verbindung zu einer äußeren Klasse, dern Zustand für Dich relevant ist.
b) (mit durch a bedingt) Du speicherst keine Werte in der äußeren Klasse. Also in Deinem Beispiel: Kein string1, das es sich daher holt.
 

Zurück
Oben