Code auslagern

fireGlurak

Aktives Mitglied
Hallo zusammen,
ich möchte gerne bestimmten Code auslagern, da dies an verschiedensten Stellen benötigt wird.

Es handelt sich hierbei um keine objektspezifische Daten und mir schweben da im Moment 2 Varianten im Kopf herum.
Ich weiß allerdings nicht genau welches besser wäre bzw. welche Frage ich mir dazu stellen sollte? 🙁

Ich habe dazu mal ganz vereinfachte Besipiele erstellt

Möglichkeit_1: Daten über ein statische Methode einer weiteren Klasse auslagern
Code:
public Class Helper   
    public static Data getData() {
            Data data = new Data();
            return data;
      }
}   

public class ClientA {
      Data data = Helper.getData();
}

public class ClientB {
     Data data = Helper.getData();
}

Möglichkeit_2: Lösen über Interfaces


Code:
public interface IData {
    Data getData();
}

public class InterfaceImpl implements IData{
      @Override
      public Data getData() {
            Data data = new Data();
            return data;
      }
      
public class ClientA {
      IData iData = new InterfaceImpl();
      Data data = iData.getData();
}

public class ClientB {
      IData iData = new InterfaceImpl();
      Data data = iData.getData();
}
}

Über Hilfe bin sehr dankbar 🙂
 
Oder einfach über einen Konstruktorparameter im Konstruktor von ClientA und ClientB?
Du musst dich immer fragen: Ist das, was ich da in ClientA und ClientB haben will, eine Abhängigkeit von diesen beiden Klassen? Und: Will ich ClientA und ClientB auch unit-testen und dann eventuell einen Mock von `Data` im Test erzeugen und den ClientA und ClientB Instanzen "reingeben"?
Deswegen: Bitte _niemals_ statische Factory-Methoden direkt in den Nutzern der erzeugten Objekte aufrufen, sondern immer von außen orchestrieren.
 
Noch kleiner Zusatz: Es macht auch wirklich überhaupt keinen Unterschied, ob du nun innerhalb von ClientA/ClientB schreibst:
Java:
Data data = Helper.getData();
oder:
Java:
IData iData = new InterfaceImpl();
Data data = iData.getData();

Beides ist gleichermaßen schlecht und du gewinnst durch die zweite Variante ja auch nichts. Egal, wieviele Abstraktionsschichten und Delegationen du da hinzufügst: In jedem Fall muss der _Nutzer_ der Abhängig (also ClientA und ClientB) ja über die _ganze konkrete_ Klasse von InterfaceImpl Bescheid wissen und auch selbst erzeugen und somit für sich schon festlegen, welche Instanz von Data er dann durch dieses InterfaceImpl bekommt. Also das Interface bringt hier nichts, weil die abhängige Entität (ClientA bzw. ClientB) effektiv nicht vom Interface abhängt, sondern von der konkreten Implementierung, also von InterfaceImpl und damit auch von dem, was InterfaceImpl.getData() liefert. Interfaces führt man ein, um Flexibilität in Form von Polymorphismus zu erreichen. Dazu darf aber nicht ClientA/ClientB von der konkreten Implementierung abhängen, sondern nur vom Interface (welches man dann von außen reinreicht).

Hier ist das "Dependency Inversion Principle" verletzt:
"High-level modules should not import anything from low-level modules. Both should depend on abstractions (e.g., interfaces)."
"Abstractions should not depend on details. Details (concrete implementations) should depend on abstractions."

weil deine Implementierung (ClientA/ClienetB) eben nicht von Abstraktionen abhängt, sondern von konkreten Implementierung.
 
Hallo und danke für die ausführliche Antwort 🙂
Mit dem Dependency Inversion Principle müsste ich mich evtl. nochmal näher mit beschäftigen 😀

Zu meinem Beispiel mit dem Interface muss ich noch eine korrektur vornehmen:
In meiner Anwendung würde das Interface an Client A/B per Injection reinkommen, also von "außen" (dachte zunächst würde keine Rolle spielen, deswegen habe ich das so vereinfacht dargestellt).

In dem Fall wäre es dann schon ein gängiger Weg wenn ich darüber nachdenke, da...

In jedem Fall muss der _Nutzer_ der Abhängig (also ClientA und ClientB) ja über die _ganze konkrete_ Klasse von InterfaceImpl Bescheid wissen und auch selbst erzeugen
...hierbei dann ja nicht mehr zutrifft.
Und...

Oder einfach über einen Konstruktorparameter im Konstruktor von ClientA und ClientB?
...ist damit wohl auch gemeint gewesen!?
 
Achso und

Du musst dich immer fragen: Ist das, was ich da in ClientA und ClientB haben will, eine Abhängigkeit von diesen beiden Klassen
Naja also das was ich von getData (hier spielen sich im wesentlichen Serviceaufrufe im Background statt) bekommen möchte ist Abhängig von den Eingangsparametern die ich über getData mitgebe (im Beispiel nicht beachtet).

Will ich ClientA und ClientB auch unit-testen und dann eventuell einen Mock von `Data` im Test erzeugen und den ClientA und ClientB Instanzen "reingeben"?

Ja guter Punkt, ist zwar aktuell noch nicht geplant, kann aber in der Zukunft vill. doche eine Rolle spielen.
 
Mit dem Dependency Inversion Principle müsste ich mich evtl. nochmal näher mit beschäftigen 😀
Der einfachste Fall wäre Konstruktor Injektion:
Java:
public class ClientA {
    private Data data;
 
    public ClientA(Data data) {
        this.data = data;
    }
 

}
Das war's auch schon. Klare Verhältnisse. Die Klasse ClientA sagt, dass sie "Data" als Abhängigkeit benötigt und du übergibst die von außen, beim Erzeugen der Instanz. Damit kannst du zum Testen auch eine Mock-Data-Implementation übergeben (sofern "Data" ein Interface ist).

"Data" kannst du natürlich durch eine beliebigen anderen Typen ersetzen, z. B. einen DataService, der sich irgendwo die Daten her holt.
 
Zuletzt bearbeitet:

Neue Themen


Zurück
Oben