Programmstruktur für "statische" Methoden


#1

Hallo,
ich habe mal eine Frage rein zur Umsetzung von bestimmten Sachverhalten.

Ich habe ein Programm welches Aufgaben je nach Funktion in eigene Klassen auslagert. Diese Klassen sind aktuell noch so angelegt, das diese einfach nur statische Methoden verwenden. Zum Beispiel würde theoretisch eine Klasse so aussehen:

    public class FunktionssammlungA{
        public static void gibMeldungAus(String meldung){
            System.out.println(meldung);
        }
   
        public static String erweiterDenString(String txt){
            return "--" + txt + "--";
        }
    }

Der Code ist natürlich stark vereinfacht.
Jetzt habe ich was von Singleton gelesen. Bein Singelton haben sich aber in den Beiträgen die Geister gestritten, ob das Sinn oder Unsinn ist so vor zu gehen. Des weiteren kann man in Interface keine statischen Methoden definieren, welche man ja bei Singelton braucht (getInstance()).

Warum ich jetzt an den Punkt komme das wieder anzugreifen ist, dass ich teile dieser Funktionssammlungen so auslagern will, das die Funktionen an sich zwar vorgegeben (Methodennamen und Parameter) sind aber der Inhalt/Logik je nach Implementierung unterschiedlich sein kann (Plugin-Prinzip). Hier kommt ganz klar das Stichwort “Interface” zu tragen.

Ich möchte vermeiden, das von meinen “Funktionssammlungen” mehr als eine Instanz erzeugt wird (Singelton), es aber zugleich möglich sein soll, das ich für diese “Funktionssammlungen” ein Interface definieren kann.

Warum bestehe ich auf die einmalige Instanzierung. Für die Instanzierung müssen bei mir immer zahlreiche Parameter und Informationen mit übergeben werden, welche Teilweise auch nur bei Programmstart bekannt sind. (diesen Sachverhalt habe ich im oberen Beispiel der Einfachhalber weg gelassen) Ich will unter allen Umständen vermeiden, das ich zum Funktionsaufruf jedes mal eine Instanz erstellen muss, das wäre zu viel Offset.

Ich muss auch noch mal ganz klar sagen, das bei den “Formelsammlungen” keine Objekt individuellen Informationen gespeichert werden. Alle privaten oder öffentlichen Variablen müssen bei allen Instantzen der Klasse identisch sein.

Welche Möglichkeiten habe ich in Java, um mein Vorhaben Java-Konform abbilden zu können?

Danke für eure Hilfe
Grüße Hans


#2

Dein Programm besteht also nur aus Aufrufen von Methoden einer Methodensammlung, die obendrein auch noch alles zusammenfasst?
Das klingt für mich erst mal merkwürdig. Java ist doch eine OOP-Sprache. Warum die unbegründete Angst, dass dir der Speicher zugeht? Es gibt sehr mächtige Java-Programme, die mehrere Megabyte groß sind. Glaubst du, dass die auf einer Methodenbibliothek fußen?
Es ist doch Sinn und Zweck einer OOP-Sprache, mit in sich geschlossenen Objekten zu arbeiten. Implementationsdetails sind dabei völlig irrelevant.

Ich habe Schwierigkeiten, hier nichts anderes als einen Designfehler zu sehen. Also was ist dein wirkliches Problem?


#3

Strategy Pattern? Verhalten dann dynamisch anpassen?

Die Notwendigkeit eines Singletons sehe ich hier auch nicht unbedingt. Aber da warte ich mal deine Antwort ab.


#4

an dieser Stelle habe ich keine Angst vor Speicherproblemen. Absolut nicht. Die trennung der Funktionen habe ich nur deswegen vorgenommen, damit diese nach Funktionsgebieten getrennt sind. Wenn du so willst, der Übersicht halber. Um meine Objekte zu erzeugen benötige ich jede mal sehr viele Informationen, welche als Grundlage für die einzel Funktionen dienen. Diese sind ab Programmstart immer gleich. Ich will zum einen vermeiden, das ich beim Instanzieren immer wieder alle Informationen zusammen tragen muss und zum anderen ist die Mehrfach-Instanzierung aus meiner sicht absolut überflüssig, da es an sich Objekt unabhängige Funktionen sind. Die Funktionen greifen nur auf Variablen aus der Klasse zurück, welche aber bei jeder Instanz der Klasse 100% gleich sein müssen.

Im Prinzip könnte man es sich so beschreiben. Ich erzeuge einen Menschen (Instanz), dieser kann auf 5 definierte Fragen antworten. Jetzt stelle ich dem Menschen eine der Fragen und bekomme eine Antwort. Wenn ich auf eine weitere Frage eine Antwort haben will, dann frage ich doch einfach wieder den bekannten Menschen, als erst aufwendig einen neuen, 100% identischen Klon zu erschaffen, der dann auf meine Frage antworten soll.

Klar könnte ich jetzt sagen ich erstelle mit Aufwand einen Menschen, schmeiße nach der Frage den Menschen weg und bastel mit bei bedarf einen neuen. Das macht aus meiner Sicht absolut keinen Sinn.

Mir ist bekannt, das Java vollständig Objekt orientiert ist. Und was das bedeutet ist mir an sich auch klar. Aber Auch in den standard Java-Funktionen gibt es aus bestimmten Gründen statische Funktionen. Zb. String.valueOf() oder ähnliches. Es macht kaum Sinn hier folgenden zu tun:

int value = 9;
String tmp = new String();
System.out.println(tmp.valueOf(value));
tmp = null;

Ich habe keine Funktionen/Methoden welche wirklich speziell für ein Objekt gedacht sind. Solche typischen Funktionen wir fahren(), tanken() oder ähnlich, welche sich auf eine Instanz vom Typ Auto bezieht, wie man sie immer bei den OOP Tutorials vorgesetzt bekommt, habe ich hier nicht.

Grüße Hans

Edit:
Ich möchte garnicht behaupten, dass das was ich mache bzw gemacht habe richtig ist. Nein wird es nicht sein. Dafür bin ich ja auch hier. Ich will lernen wie es richtig geht. Aber das was ich mache, muss zumindest in dem Moment, wo ich es mache für mich Sinn ergeben. Und für die Funktionen, welche ich habe immer mit Aufwand ein neues Objekt zu erzeugen, ist in meinen Augen nicht logisch, wenn es Design-Pattern wie Singelton gibt.


#5

Du willst verhindern, dass immer neue Objekte erstellt werden, wenn schon ein passendes existiert? :thinking:

if(meinObjekt == null) {
   meinObjekt = new MainObjekt(/*Parameterliste*/);
}

meinObjekt.machwas();

Aber ich habe das Gefühl, dass du das nicht suchst…


#6

@L-ectron-X
Diese Methode ist mir bekannt, um im laufenden Quellcode zu verhindern, das ich ein bereits instanziertes Objekt mit einer neuen Instanz überschreibe.

Was mich Gedanklich festhält, ist das statische Elemente einer Klasse im Bezug auf eine VM nur einmal existieren. Sprich wenn ich folgende Klasse habe,

public class Test{
    public static int value;
}

ist der Wert von der Eigenschaft “value” immer der, welcher zuletzt gesetzt wurde. Und dabei spielt es keine Rolle ob ich die Klasse einmal instanziert habe. Ich könnte dann von überall heraus mittels

Test.value;

auf den Wert “value” zugreifen. (Und für die Kritiker unter euch, mir ist schon klar, das man auf Eigenschaften nicht direkt zugreifen sollte und dafür Setter und Getter verwenden sollte … bitte seht es mir für dieses einfach Beispiel nach).

Für mein Programm hatte, bzw habe ich noch die Vorstellung, das ich auf ähnliche Weise Funktionen zentralisieren kann um nach einer einmaligen Initalisierung immer wieder darauf zuzugreifen. Vieleicht muss ich sogar noch etwas weiter ausholen und die Rahmenbedingungen von meinem Programm zu erklären. Ich habe ein Programm geschrieben, welches Web-Services bereit stellt. Einmal als Rest, einmal als SOAP und einmal als WebSocket. Alle drei Services werden durch Java automatisch instanziert und haben von mir kein inject bekommen, um irgendwelchen Context quer zu laden. nur hat jeder Web-Service im grunde die gleichen Methoden. Diese Methoden habe ich in meine Funktionsklassen gekapselt um den Quellcode nur einmal zu haben.
Ganz vereinfacht sieht das wie folgt aus:

public class Sammlung {
    public static int sum(int a, int b){
        return a + b;
    }
}

public class SoapWs{
    public int sum(int a, int b){
        return Sammlung.sum(a, b);
    }
}

public class RestWs {
    public int sum(int a, int b){
        return Sammlung.sum(a, b);
    }
}

public class WebSocket{
    public int sum(int a, int b){
        return Sammlung.sum(a, b);
    }
}

Ich hoffe mein Vorhaben und meine Ideen werden damit etwas besser veranschaulicht.

Grüße Hans


#7

Ist a+b nicht viel einfacher, als ein Funktionsaufruf? Ich denke mal, du meinst das nur Exemplarisch.

Natürlich macht es manchmal Sinn, Funktionen statisch auszulagern, java.lang.Math wäre ein gutes Beispiel dafür. Allerdings sollten in solchen Funktionen Seiteneffekte vermieden werden, also z.B. übergebene Objekte nicht unbedingt inhaltlich verändert werden, sofern dies nicht explizit in der Dokumentation erwähnt wird. Solche Funktionssammlungen sollten aber nach Möglichkeit nur einen privaten, parameterlosen Konstruktor haben und keinerlei Objektmethoden oder Member, weil Instanzieren braucht man sie dann ohnehin nicht.


#8

Aber wäre das nicht der Ansatz von Singelton? Sowas habe ich ja mehr oder weniger vor, nur das ich leider aufgrund von gewissen Umständen bei einigen Funktionssammlungen erst einmal zahlreiche Grundlagen setzen muss, bevor alles funktioniert. Das sind Informationen aus den Configs und anderen Daten. An sich existiert das ja bereits, als statische Methoden etc…

Ich stehe damit aber gerade vor einem Problem und einem eigenen Wunsch.
Problem:
Ich muss jetzt dafür sorgen, das die Funktionsklasse austauschbar wird, so wie ein Plugin. Dazu brauche ich aber Interface und in Interface kann ich keine statischen Methoden abbilden :wink:

eigener Wunsch:
Da ich weiß, dass das mit statischen Methoden eigentlich unsauber ist, möchte ich was dazu lernen, wie es “richtig” geht. Deswegen ja auch hier diese Grundsatzdiskussion. Entsprechend auch ein großes Danke an alle, welche mir hier helfen, den richtigen Weg zu finden :slight_smile:


#9

Der Unterschied zwischen Sammlungen statischer Funktionen und Singletons ist der, dass Singletons öffentliche Member-Methoden - also nicht statische - haben. Singletons müssen deswegen Instanziert werden und das geschieht meist in der Initialisierungs-Methode der Klasse. Singletons sollten auch nur eine einzige öffentliche statische Methode haben - “getInstance()” - und diese gibt die einzig existierende Instanz der Klasse zurück, welche wiederum “static private” in der Klasse selbst gehalten wird.

Ich persönlich halte mich gewissenhaft aus Singleton-Grundsatzdiskussionen heraus. Das solche irgendwie Konfiguriert werden müssen, ist klar, aber warum sie deswegen ein Anti-Pattern sein sollen nicht.


#10

Das Problem sind nicht die statischen Methoden an sich, sondern die Verwendung derer.

void m1(String meldung) {
  String a = FunktionssammlungA.erweiterDenString("Hello World");
  FunktionssammlungA.gibMeldungAus(a);
}

Typische Verwendung des ganzen. Aber wie testet man nun, wenn man TDD macht.
Die einzelnen statischen Methoden sind einfach zu testen.

Bei Methode m1 wird es schwierig. Hier wird von innen heraus auf FunktionssammlungA zugegriffen. Es wird sich eine Instanz geholt, wenn diese auch static ist und darauf werden dann Funktionen aufgerufen.

Der einzige Weg um das zu testen ist FunktionssammlungA an die Methode oder die Instanz in der m1 lebt zu übergeben und dann darauf die methoden aufrufen. Das kann man sehr einfach mocken und dann mechanisch überprüfen.

void m1(String meldung, FunktionssammlungA fs) {
  String a = fs.erweiterDenString("Hello World");
  fs.gibMeldungAus(a);
}


public class Test{
    public static int value;
}

Kann man ändern, allerdings nur global und sorgt dann für Concurrency-Probleme

void x() {
  Test.value = 5;
  System.out.println(Test.value);
}

In einer Multithread-Umgebung kann hier niemand sagen, was x ausgibt, da jeder andere Thread value nach dem Ändern und vor der Ausgabe wieder in was anderes ändern kann. Also immer eine ganz schlechte Idee, gerade wenn man dann Services anbieten möchte.

public class WebSocket{
    private final Sammlung s;
    public WebSocket(Sammlung s) {
      this.s = s;
    }

    public int sum(int a, int b){
        return s.sum(a, b);
    }
}

void static main(String[] args) {
  Sammlung s = new Sammlung();
  WebSocket ws = new WebSochet(s);
  RestWs rest = new RestWs(s);
  ...
}

Wobei Dependency Injection auch Singleton kann. @Inject und @Singleton

@Singleton
public class Sammlung {}

class XXXWs {
  @Inject Sammlung s;
}

#11

Ja genau so habe ich mir das vorgestellt. Über die statische öffentliche Methode “getInstance()” rufe ich die einzige Instanz ab und kann dann auf die öffentlichen Methoden der Instanz zugreifen.

Implementieren wollte ich das so:

public class Funktionssammlung{
    private static volatile Funktionssammlung INSTANCE;
 
    public static Funktionssammlung getInstance(){
        if (INSTANCE == null) {
            synchronized (Funktionssammlung.class){
                INSTANCE = new Funktionssammlung();
            }
        }
        return INSTANCE;
    }
}

Aber da ich die Funktionssammlung gerne mittels Interface definieren möchte bzw muss, benötige ich noch eine Idee, einen Weg umd eine statische Mehtode in das Interface zu packen.


#12

Das Problem wird weniger sein die statische Methode in das Interface zu stecken (was überaupt kein Problem ist :wink: ) sondern eine private Instanz zu speichern.

Allerdings musst du es sowieso erweitern, da du so ja nur eine genaue Implementierung hast und nicht, wie du es angedeutet hast, eine Art Plugin Möglichkeit zu bieten.
Mal davon abgesehen, dass du alles (ja ich weiß Mehrfachvererbung nicht) was du mit Interfaces machen kansnt auch einfach mit einer (abstrakten) Klasse machen könntest


#13

Die Antwort von ionutbaiu war schon abzusehen. Ich hätte es ansonsten der Vollständigkeit halber erwähnt. Aber mal allgemein ein paar Punkte:


Singleton:

Das Muster erfüllt NICHT den Zweck, “irgendwelche praktischen Hilfs-Funktionen überall anzubieten”. Das Ziel vom Singleton ist: Sicherstellen, dass es nur eine Instanz von einer Klasse gibt, von der es nur eine Instanz geben darf. Das Ziel des Musters ist nicht ein “praktisches Vefügbar-machen”, sondern die zwingende Einschränkung auf eine Instanz.

Nebenbei: Ich habe Singleton IIRC praktisch noch nie benutzt. Wozu auch? Irgendeine Datenbankanbindung vielleicht. Man braucht es wirklich sehr selten.


Statische Methoden:

Ich bin ja ein “Fan” von static Methods. Die Hauptargumente, die dagegen genannt werden, sind die, die auch oben angebracht wurden: “Die kann man nicht mocken”. Ja. So what? Die entscheidende Frage ist ja: Sollen die Methoden verschiedene Implementierungen haben können? (Polymorphie/Interfaces).

Mocking ist schon eine andere Implementierung. Und wer sowas macht wie

public static Data getData() {
    Database database = connect("localhost:1234:/myData", "admin", "password");
    return database.query("bobby tables");
}

ist selbst schuld :wink: Statische Methoden sind kleine, unveränderliche und insbesondere funktionale Building-Blocks. (“Funktional” im Sinne von “Funktionaler Programmierung” : Sie bekommen Parameter, und das Ergebnis hängt nur von den Parametern ab)

Oder, an die Leute, die meinen dass die fehlende Mock-barkeit von statischen Methoden ein Problem ist:

Hat irgendjemand schonmal ein Problem damit gehabt, dass Math.sin nicht gemockt werden kann?

(Wenn man es mocken will, dann kann man immernoch ein

interface Sine { 
    default double sin(double d) { return Math.sin(d); }
}

erstellen, wo man in der default-Implementierung dann die (praktische weil wiederverwendbare weil statische) Math.sin aufrufen kann).


Das eigentliche Problem

… hab’ ich noch nicht verstanden. Es ist ja jetzt wohl so, dass es

interface Functions {
    void doThis();
    void doThat();
}

geben soll, für das es verschiedene implementierungen gibt. Und es soll irgendwo ein Objekt von so einer Implementierung geben. Und dieses Objekt soll “global” verfügbar sein. Richtig?

Den letzten Punkt hatte L-ectron-X schon hinterfragt: So eine globale Verfügbarkeit eines Objektes ist immer suspekt. Aber davon ausgehend, dass das bei dir Sinn macht, stellen sich IMHO erstmal zwei wichtige Fragen:

  • Wer ist dafür verantwortlich, wann genau diese Instanz zu erstellen, und woher weiß derjenige, welche konkrete Implementierung er erstellen soll, und welche Parameter die braucht?
  • Was spricht dagegen, diese Instanz in einer anderen Klasse verfügbar zu machen? (Du meintest, dass man in interfaces keine static methods verwenden könnte, und das ein Problem wäre, aber das ist eigentlich nicht so)

(Eine theoretische Möglichkeit, von der ich selbst noch nicht 100% überzeugt bin, wäre auch, dass man die statischen Methoden anbietet, sie aber intern (transparent für den Aufrufer) an eine Instanz der passenden Implementierung weiterreicht - aber auch da stellt sich die erste der beiden genannten Fragen…)


#14

Ich hab nicht vollständig gelesen… [/offtopic]

DAS und zum Beispiel java.util.Random als Singleton ist ein gutes Beispiel dafür!

Mit der Fabrikmethode geht das so:

class WichtigeKlasseMitWichtigerVar {

    private static WichtigeKlasseMitWichtigerVar wkmwv;
    public int wichtigeVar = 555;

    private WichtigeKlasseMitWichtigerVar() {
    }

    public static WichtigeKlasseMitWichtigerVar newWichtigeKlasseMitWichtigerVar() {
        if (wkmwv == null) {
            wkmwv = new WichtigeKlasseMitWichtigerVar();
        }
        return wkmwv;
    }

    public int wichtigeMet() {
        return wichtigeVar + wichtigeVar;
    }

    /**
     * Programmeinstieg steht irgendwo... Und hat einen langen Bart....
     *
     * @param args
     */
    public static void main(String[] args) {
        System.out.println(newWichtigeKlasseMitWichtigerVar().wichtigeVar);
        System.out.println(newWichtigeKlasseMitWichtigerVar().wichtigeMet());
        System.out.println((newWichtigeKlasseMitWichtigerVar().wichtigeVar = 1));
        System.out.println(newWichtigeKlasseMitWichtigerVar().wichtigeMet());
    }
}

Ausgabe wäre:

555
1110
1
2

Ich würd dazu raten, dass konsequent so zu machen. Falls wichtige Variable unveränderlich sein soll, dann final. Getter und Setter sind hier anscheinend auch Humbug, wie du bereits (richtig) erwähnt hast.


#15

@CyborgBeta Üblicherweise versuche ich ja, Beiträge nur dann zu löschen, wenn sie off-Topic oder Spam sind (und davon schreibst du auch genug). D.h. üblicherweise versuche ich, Beiträge nicht zu löschen nur weil ich ihnen inhaltlich/fachlich widerspreche. Im Gegenteil: Für Diskussionen über sowas ist ein Forum da! Aber bei obigem Beitrag bin ich mir nicht sicher, in welche Kategorie er fällt.


#16

Das ist kein Problem:

public interface Funktionssammlung {
// Interface-Methoden und -Konstanten
}

class Functions implements Funktionssammlung {
  private static final Functions INSTANCE;

  static {
    Config c = Something.readConfig(...) // denk dir was aus
    INSTANCE = new Functions(c);
  }

// Member

  private Functions{Config c) {
   // Konfiguration der Instanz
  }

// Member-Methoden (nicht statisch!)

  public static Functions getInstance() {
    return INSTANCE;
  }
}

Das einzige Problem dabei ist, dass ein solches Singleton trotzdem mehrfach instanziert werden kann, wenn man dies über verschiedene Classloader, die voneinander nichts wissen, in mehreren Threads tut, das geht aber recht tief in JVM-Details.

@ionutbaiu: Das Testen von m1 ist in diesem Falle gar nicht so schwer - teste einfach die statichen Methoden vor m1.

@CyborgBeta: Random als Singleton? [ironie]Super Idee.[/ironie]

@all: Wieso funktioniert der Code-Tag nicht mehr?


#17

Naja, Getter und Setter sind nicht per se schlecht. Es kommt auf den Anwendungsfall an… Aber hier klang es so, als wenn diese nicht unbedingt benötigt werden… Mehr wollte ich damit nicht sagen.

Bitte nix löschen.


#18

Tschuldigung, ich meinte: http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/lang/Math.java#Math.random()

Code-Tag(s): Es gab ein Update… (vielleicht eine Kausalität)


#19

OT: Haben Code-Tags hier überhaupt je funktioniert? Das ist doch dieses Markdown-Muster. Einfach den Code um 4 einrücken, bzw. auswählen und auf den </>-Button klicken.


#20

Hab meinen Beitrag überarbeitet und etwas “entschärft”… Hoffentlich muss er nicht dennoch gelöscht werden.

Edit: (Java-)Code kann gesetzt werden in: Jeweils dreimal `-Zeichen.