Statische Getter/Setter?

In unserem Legacy-Code findet sich ein seltsames “Pattern”: Es gibt kontext- oder konfigurationsartige Klassen, die von vielen anderen Klassen verwendet werden, und mehr oder weniger wie Maps aufgebaut sind. Um auf die Daten in diesen Kontext-Objekten zuzugreifen, gibt es Hilfsklassen mit statischen “Gettern”/“Settern”:

class Util {
   static Foo getFoo(Context ctx) { ... }
   static void setFoo(Context ctx, Foo foo) { ... }
   //u.s.w.
}

Ich denke, dass das getan wurde, um die Kontext-Klassen allgemein zu halten (so dass sie nicht die Keys der in ihnen gespeicherten Objekte kennen müssen). Trotzdem finde ich den Code furchtbar.

  • Gibt es irgendwelche Voraussetzungen, unter denen dieses Pattern okay sein könnte?
  • Wie würdet ihr das schreiben, wenn ihr ganz von vorn anfangen würdet?
  • Gegeben dass diese Methoden von sehr vielen Klassen aufgerufen werden, was wäre der “schonendste” Weg, das zu bereinigen?

Möglicherweise. Mir fällt spontan aber kein Fall ein.

Dazu fehlt wohl etwas Kontext ;-)[quote=„Landei, post:1, topic:19231“]
Gegeben dass diese Methoden von sehr vielen Klassen aufgerufen werden, was wäre der „schonendste“ Weg, das zu bereinigen?
[/quote]
Wahrscheinlich würde ich die Context-Klasse erweitern und die Getter/Setter dort implementieren. Die statischen Methoden würde ich deprecaten. Dadurch, dass die Klasse erweitert wird, ist sie nach wie vor mit der Util-Klasse einsetzbar. Dort wo die spezifische Klasse bekannt ist, könnten die richtigen Getter/Setter eingesetzt werden.

Sobald man keine Stellen mehr im Code hat, an denen die statischen Methoden verwendet werden, könnte man aus der Vererbung eine Komposition machen.

Fixed :smiley:

Was diese Context-Klasse ist, und wie sie verwendet wird, wäre hier wohl wirklich das relevanteste. Wenn das wirklich nur so etwas wie eine Map ist, dann könnte das „“„Ziel“„“ hinter diesem Konstrukt sein: Den Context abstrakt und Applikations-unspezifisch zu halten, und ihm über die Utils den Applikationsspezifischen Teil „überzustülpen“.

Ich habe zumindest auch schon Anwendungen gesehen, wo etwas, was im Endeffekt eine Map<String, Object> war, in praktisch (fast) JEDEN (!) Konstruktor übergeben und global-galaktisch praktisch überallhin weitergereicht wurde. Jeder hat „irgendwelches Zeug“ in diese Map gelegt, und an anderer Stelle wurde „irgendwelches Zeug“ aus dieser Map rausgeholt. Natürlich waren die Key-Strings unspezifiziert und willkürlich. Wenn man so etwas sieht, dann könnte das, was du jetzt (zu Recht!) noch hinterfragst ja schon ein erster Aufräum-Schritt sein:

Vorher:

class Application {
    public Application(Map<String, Object> settings) {
        settings.put("language", Locale.ENGLISH);
        component = new Component(settings);
    }
}

class Component{
    public Component(Map<String, Object> settings) {
        settings.put("foregroundColor", Color.RED);

        // I know it's in there....
        locale = (Locale)settings.get("language");
        ...
    }
}

Nachher:

class Application {
    public Application(Context context) {

        Utils.setLanguage(context, Locale.ENGLISH);
        component = new Component(context);
    }
}

class Component{
    public Component(Context context) {
        Utils.setForegroundColor(context, Color.RED);
        locale = Utils.getLanguage(context);
        ...
    }
}

Was zumindest schonmal den Schritt von „stringly typed“ zu „strongly typed“ ermöglichen würde.

Darüber hinaus gehende „Refactorings“ sind natürlich um Größenordnungen schwieriger. Je nachdem, wie kagge der ganze Rest ist, kann der Versuch, so ein (prominent und überall verwendetes) „Ding“ da rauszuziehen vergleichbar sein mit dem Versuch, sich Kaugummi aus den Haaren zu entfernen: Abschneiden. Hilft alles nix.

Tja, so ungefähr habe ich mir das auch schon gedacht. Da wird mir wohl nichts anderes übrig bleiben, als das mal in größerer Runde auszudiskutieren, sonst kommen wir da nie von weg. Ich fürchte nur, dass es wie immer “wichtigeres” gibt.