Programmierstil: Objektmethoden vs. Klassenmethoden

Hallo Forum,

als Java-Anfänger habe ich festgestellt, dass in entsprechenden Einführungsbüchern immer schnell die Objetorientierung hervorgehoben wird und die Codebeispiele eigentlich fast immer auf die Erzeugung von Objekten abzielen. Ich habe allerdings sehr viele Funktionen, die keinen Objekten zugeordnet sind, d. h. in meinem Code wimmelt es nur so vor lauter „static“. Ich habe den Verdacht, dass das auf einen weniger eleganten Programmierstil schließen lässt und frage euch, ob ihr dem zustimmt. Meine Logik bisher ist folgende: wenn ich mehrere Objekte einer Klasse benötige, erzeuge ich natürlich diese Objekte mit entsprechenden Obgjektmethoden. Wenn feststeht, dass ich eigentlich nur Klassenmethoden benötige (z. B. nur ein allgemeiner Rechenalgorithmus) verzichte ich auf die Erstellung eines Objektes und verwende hier nur statische Methoden. Oder brächte es irgendwelche Vorteile, hier trotzdem ein Objekt zu erzeugen und Klassenemthodenm wann immer es geht zu vermeiden? Ihr wisst das bestimmt! :slight_smile:

VG!

Ein Argument für static hast du schon genannt: Wenn eine Methode unabhängig von einer konkreten Instanz ist, kann man sie static machen.

Allerdings verlierst du mit static auch die Möglichkeit, deine Methode durch Vererbung zu spezialisieren oder hinter ein Interface zu kapseln.

Gruß

statische methoden sind schwerer zu testen, wenn sie selbst noch weitere Logik aufrufen/verwenden

Ja, dazu hatte ich schonmal einen Thread eröffnet, mit einer langen, interessanten, fruchtbaren, vielschichtigen und aufschlußreichen Diskussion - und demnächst wird dieser Thread leider hoffentlich gelöscht.

Deswegen nochmal hier:

Es gibt wohl keine pauschalen, verbindlichen, formalen Regeln dazu. Das Beispiel, das ich da mal angeführt hatte, waren z.B. die Methoden [japi]Collections#shuffle[/japi] oder [japi]Collections#sort[/japi]. Man könnte ja fragen: Warum kann man nicht einfach someList.sort(); aufrufen, sondern muss Collections.sort(someList); verwenden? Wenn “sort” eine Membermethode wäre, wäre das Objektorientierter. Jede List-Implementierung könnte sich aussuchen, WIE genau sie die Sort-Methode implementiert (und da gibt es wichtige Unterschiede - eine LinkedList zu sortieren ist was ganz anderes, als eine ArrayList zu sortieren!).

Ich persönlich habe einen starken Hang zu statischen Utility-Methoden. Das hat verschiedene Gründe:

  • Die Interfaces bleiben “schmal”. Wieder das List-Beispiel: Sollte man die Methoden “sort” und “shuffle” ins List-Interface mit aufnehmen? Ja, vielleicht… Und “reverse”? Naja … Und “permute”? Äh… irgendwann ist’s aber auch mal gut, es gäbe 1000 Methoden, die da “irgendwie reinpassen” würden…
  • Es ist SEHR leicht, aus einer statischen Utility-Methode eine Membermethode zu machen. Umgekehrt ist das u.U. schwierig bis unmöglich. Angenommen, man wollte die Methode “sort” nachträglich ins List-Interface aufnehmen, dann könnte man in einer Implementierung einfach schreiben
@Override
public void sort()
{
    Collections.sort(this);
}
  • Das letzte wird bei Java 8 sogar “zum System erhoben”, durch die default methods. Infos gibt’s z.B. unter http://www.angelikalanger.com/Lambdas/LambdaTutorial/lambdatutorial_5.html (Die hatten während ihrer Entstehung verschiedene Namen, man findet auch einiges unter “defender methods”, siehe z.B. diese PDF: http://cr.openjdk.java.net/~briangoetz/lambda/Defender%20Methods%20v3.pdf ). Damit kann man Interfaces nachträglich erweitern - eben genau indem man für eine neu eingefügte Methode eine statische Utility-Methode als “default”-Implementierung angibt (man kann den Code auch direkt hinschreiben, ich erwähne nur, dass man so schon existierenden Code wiederverwenden kann).
  • Statische Utility-Methoden verändern i.d.R. keinen Zustand, und sind demnach automatisch und offensichtlich Thread-Safe

Man könnte noch einge Details und pros/kontras ausbreiten (dafür reicht meine Mittagspause aber gerade nicht ;)). Und ich weiß, dass es OO-Puristen mit guten (!) Argumenten gibt, die gegen statische Utility-Methoden sprechen. Im speziellen, dass damit keine Polymorphie mehr möglich ist. Aber mit den oben angedeuteten Punkten wird zumindest dieses Argument IMHO entkräftet.

Letztendlich hängt es natürlich auch von offensichtlichen Dingen ab, wie etwa, ob die Methode etwas machen soll, wofür sie auf die privaten Interna einer Klasse zugreifen muss (d.h. ob sie z.B. eine private Variable verändern muss). Die Argumente beziehen sich auf die Fälle, wo man die Wahl hat, und einem beide Möglichkeiten auf den ersten Blick “gleich gut” erscheinen.

Vielleicht modellierst du einfach noch zu grob.

Ich hatte auch in den ersten Jahren viele statische “Funktionsbibliotheken”, so a la Helper.berechne(a,b,c)

So etwas läuft schnell Amok, dann hast du eine Helper-Klasse mit 100 statischen Methoden (etwa wie Math)

In vielen Fällen (oder: manchmal?) ist es besser, a,b,c in ein Objekt zu packen und auf diesem Objekt dann o.berechne() aufzurufen. Wenn du solche “kleinen” Objekte gezielt einsetzt, dann kannst du damit deinen Code und die Logik besser strukturieren (packages), gewinnst Flexibilität usw. Allerdings reden wir hier wirklich von OO-Design, ein Gebiet, das ebenso riesig wie undurchschaubar ist. Leider gibt es da keinen klaren Regeln und Vorschriften, vieles ist Geschmackssache (und hängt von Kosten/Nutzen-Erwägungen ab). Lies mal “Head First Pattern” oder ein ähnliches Buch über Entwurfsmuster, wenn du mal verschiedene Anwendungsfälle für Objekte kennst dann ist es leichter, diese zu verwenden.

Im Extremfall hast du eine statische main, Helper-Objekte mit vielen vielen statischen Methoden - diese funktionieren dann so wie die guten alten Funktionsbibliotheken und du machst im wesentlich C mit aufgeblähter Syntax. Das sollte nicht das Ziel sein.

Ich stimme dir da zu, dein Stil ist wohl noch nicht Objektorientiert.

Einer der Grundsaetze der OO:
Daten und die funktionen die damit arbeiten sollten im selben Objekt sein.

Das ist natuerlich sehr allgemein ausgedrueckt, aber grundsaetzlich kann das eben ein Symptom dafuer sein, dass du noch nicht OO denkst bzw. programmierst.

Es gibt tatsächlich Gründe, Methoden statisch zu machen, aber in der Praxis ist der häufigste Grund “Faulheit”. Sobald die Projekte größer werden, getestet werden müssen und/oder flexibel eingesetzt werden sollen, ist das Kuddelmuddel da.

Ich würde dir empfehlen, dich einmal mit der professionellen Lösung für dieses Problem zu beschäftigen, nämlich Dependency Injection. Zum Einstieg würde ich zu Guice raten (damit sind später Spring und CDI dann leichter zu verdauen). Wenn du einmal verstanden hast, wozu das überhaupt gut ist, und selbst einmal gesehen hast, wie die Anwendung von DI eine Applikation sozusagen “automatisch” besser strukturiert, willst du dein static sicher nicht zurück.

Ich denke, dass statische Utility-Methoden viele Vorteile haben (und lasse den impliziten Vorwurf der Faulheit als Grund für diese Ansicht nicht gelten (was nicht heißen soll, dass das nicht in anderer Hinsicht zutrifft ;)). Natürlich IST es eben gerade NICHT OOP, wenn man nur ein paar Pojos hat, und ansonsten nur mit statischen Methoden da drüberjuckelt. Aber ganz subjektiv könnte man das erstmal runterbrechen auf die Frage, welche Methoden denn in ein Interface aufgenommen werden sollten. Ich finde, es könnte da eine Art „axiomatische“ Vorgehensweise geben (die etwa dem entspricht, was ich angedeutet habe), bei der jedes Interface die minimale Menge an Methoden hat, die es zum Leben braucht. Das andere Extrem ist, dass man für jeden auch noch so speizellen Sche!ß eine Methode ins Interface packt. Sicher liegt die Wahrheit irgendwo dazwischen (~„Die Methoden, die man braucht, um es leicht und bequem verwenden zu können“, ~„Die Methoden, die man (vielleicht?!) überschreiben (können?) will“…), aber wenn jemand harte, sinnvolle Kriterien nennen könnte, wäre ich sehr dankbar. (Und … bald meinen Job los, aber … da hab’ ich nicht so viel Angst :smiley: )

Das Problem beim Java static ist, dass es mehrere Bedeutungen hat:

  1. Eine Methode soll ausserhalb des normalen Instanz-Kontextes laufen (also ohne Beziehung zu einem konkreten Objekt), entspricht einer Funktion bei funktionaler Programmierung.
  2. Eine Methode soll auf Klassenebene funktionieren, d.h. der Kontext des Methodenaufrufs ist die Klasse selbst (und nicht wie bei normalen Methoden eine Instanz der Klasse).

Ursache dafuer ist, dass es kein this innerhalb einer statischen Methode gibt. Auch kein super. Damit ist keine Vererbung/Polymorphie von Klassenmethoden moeglich. Hier ein kleines Beispiel:

public class Test {
    public static class Super {
        public static String getStatic(){
            return "SuperStatic  ";
        }
        public String getInstance() {
            return "SuperInstance";
        }
        public static String getStaticWithCall() {
            return getStatic();
        }
        public String getInstanceWithCall() {
            return getInstance();
        }
    }

    public static class Sub extends Super {
        public static String getStatic() {
            return "SubStatic    ";
        }

        @Override
        public String getInstance() {
            return "SubInstance  ";
        }
    }

    public static void main(String[] args) {
        Super superInstance = new Super();
        Sub   subInstance   = new Sub();
        System.out.println("superInstance: " + superInstance.getInstance() + " / " + superInstance.getInstanceWithCall() );
        System.out.println("subInstance:   " + subInstance.getInstance()   + " / " + subInstance.getInstanceWithCall()  );
        System.out.println("Super:         " + Super.getStatic()           + " / " + Super.getStaticWithCall() );
        System.out.println("Sub:           " + Sub.getStatic()             + " / " + Sub.getStaticWithCall() );
    }
}

und die Ausgabe:

superInstance: SuperInstance / SuperInstance
subInstance:   SubInstance   / SubInstance  
Super:         SuperStatic   / SuperStatic  
Sub:           SubStatic     / SuperStatic 

Problematisch ist der letzte Aufruf (Sub.getStaticWithCall).

In richtigen OO-Programmiersprachen ist sowas uebrigens moeglich. Z.B. bei Ruby hat eine Methode auf Klassenebene ein ganz normales this (heisst dort self), nur zeigt das bei einer Klassenmethode eben auf die Klasse und nicht auf ein Objekt. Damit kann man diese auch vererben und alles tun, was mit normalen Methoden auch geht.

Das ist unglaublich praktisch, da man sich ca 99% aller „*Manager“-Klassen sparen kann, denn da sind ja meistens Methoden drin, die Instanzen der gemanagten Klassen verwalten. Diese kann man auch als Klassenmethoden direkt schreiben.

Fuer mich bleibt der Grundsatz: Niemals static Methoden! (es sei denn, es geht anders gar nicht oder wird zuu wirr). :wink:

Vielen Dank für euer Meinungsbild, das ich für mich mal so zusammenfasse: die Verwendung von static muss nicht immer als Unart gelten, es lohnt sich aber, sie weitgehend zu reduzieren. Ich werde in Zukunft versuchen, Klassenmethoden dann einzusetzen, wenn es nötig ist, und sie dann zu vermeiden, wenn es möglich ist. Und es wird dann vermutlich so sein, dass nur noch wenige (oder eventuell keine) Fälle übrigbleiben, wo es ohne Klassenmethoden nicht geht, wenn mein Denken noch stärker in der Objektorientierung angekommen ist. Ich muss da noch Einiges lernen, habe aber jetzt schonmal eine Marschrichtung, die ich glaube, verstanden zu haben. Vielen Dank für eure Meinungen und Anregungen! :slight_smile:

Der Beginn war bei mir zumindest, dass wir erstmal mit der main angefangen haben und alle Methoden darin geschrieben. Macht man aus dieser Klasse später ein Objekt hat man die meisten unnötigen statics eliminiert und ist mE der OOP deutlich näher gekommen.

“Unnötige static-Methoden”, “Niemals static Methoden!”, … das irritiert mich etwas. (@HerrKaiser: Ich würde das “Meinungsbild” (wenn man es denn überhaupt so nennen will) zumindest als “uneindeutig” bezeichnen, aber … natürlich kann jeder Hinweise aus der Diskussion und daraus dann wiederum seine Schlüsse ziehen).

Wenn eine Methode nur auf den public-Elementen einer Klasse arbeitet, dann kann man sie ganz mechanisch umwandeln von

class Example 
{
    public void doSomething()
    {
        // Call some public methods of 'this'
    }
}

zu

class Example 
{
    public void doSomething()
    {
        doSomethingStatic(e);
    }
    private static void doSomethingStatic(Example e)
    {
        // Call some public methods of 'e'
    }
}

Mal unabhängig von der Frage, wann so etwas die subjektiven (!) Eigenschaften wie “schön” oder “elegant” hat: Warum sollte das per se in irgendeiner Hinsicht nachteilhaft, schlechter oder auch nur ‘weniger OOP’ sein?

Ich hatte schon gelegentlich die Daumenregel so formuliert: Fields macht man NIE static, außer, wenn man absolut sicher ist, dass sie static sein müssen (also fast nie ;)), und Methoden macht man IMMER static, wenn sie ohne Krämpfe static gemacht werden können. Das “ohne Krämpfe” bezieht sich darauf, dass man natürlich NICHT pauschal jede Methode dadurch static machen sollte, dass man ihr die Instanz als ersten Parameter mit übergibt. Aber… wenn man (speziell bei nicht-public-Methoden) einfach ein ‘static’ hinschreiben kann, d.h. dafür sonst nichts zu ändern ist, dann sollte man es IMHO tun.

Oder, mal ganz konkret und ketzerisch, von der anderen Richtung her gefragt: Welche Methoden sollte man z.B. ins List-Interface mit aufnehmen? sort? reverse? shuffle? permute? … (zum Nachhaken: … mit dem Ziel, NIE mehr irgendwelche statischen Methoden zu haben, die irgendwas mit einer ihr übergebenen List machen!?!)

Ein Problem ist z.B., dass statische Methoden in abstrakten Klassen nicht so einfach überschrieben werden können etc. Der ganze Zirkus mit der Polymorphie funktioniert dann nicht mehr richtig.

Ein anderes, dass sie (wenn sie public sind) für immer statisch bleiben müssen. Wenn du bemerkst, dass eine Methode den Zustand eines Objekts weder benötigt noch verändert - was macht die Methode dann überhaupt in der Klasse? Also machst du sie static. Später taucht dann ein Use-Case auf, der doch auf den Zustand Bezug nimmt…dann kannst du nicht mehr zurück, wenn auch andere Leute mit deinen Klassen arbeiten.

Dazu kommen Probleme mit Threads, wo das “globale” Verhalten eher unerwünscht ist.

Fazit: statische Methoden sind im Prinzip eh ein Krampf, kein Wunder dass sie in Scala einfach entfernt wurden (und durch Singleton-Objekte ersetzt wurden)

*** Edit ***

    private static void doSomethingStatic(Example e)
    {
        // Call some public methods of 'e'
    }

Wenn die Methode den Zustand von e verändert, dann wär das krass unschön (sollte eine normale Methode sein).

Wenn nicht, dann gibt es irgendeinen Seiteneffekt und die Methode ist DORT besser aufgehoben.

Die Frage stellt sich bei Java gar nicht erst, da es keine Möglichkeit gibt Implementierungen direkt im Interface anzubieten. Ansonsten würde ich so viele Methoden wie möglich mit ins Interface stecken (bzw. nur die, die benötigt werden um sich die Implementierung weiterer Methoden massiv zu vereinfachen), solange es nur sehr wenige gibt, die man selbst implementieren muss. Sieht mit Javas Syntax einfach schöner aus, ist wart- und erweiterbarer. Java 8 stellt ja nun Mittel bereit um dies zu ändern und diese werden ja auch schon eingesetzte (siehe Stream API).

Aber ich glaube ich habe mit dir da schon einmal drüber diskutiert - im vorherigen Forum.

Wie stehts denn dann um Utility Methoden?
Beispiel ist eine Utility Klasse die ich für SWT geschrieben hatte. Mit Hilfe der Klasse lassen sich alltägliche Dinge mit einem Methodenaufruf durchführen. Es ist nicht notwendig die Methode jedes mal neu zu implementieren. Ich wüsste ehrlich gesagt keine Möglichkeit das anders zu gestalten als über static Methoden.

@Bleiglanz : Das Argument der Polymorphie hatte ich ja auch schon angesprochen - und, zumindest dachte ich das: Entkräftet. Es macht in bezug auf die Polymorphie keinen Unterschied, ob man
void doSomething() /* Viel Code */ }
schreibt, oder
void doSomething() { callStaticMethodMitVielCode(); }

Ob/wann methoden public sind ist sowieso die Frage, die wie ein Damoklesschwert über allem schwebt. Auch @Antoras : Wenn etwas einmal irgendwo public (z.B. in einem Interface) steht, kann man das nicht mehr wegnehmen. Public APIs are forever. (Bisher bestand auch das Problem in der umgekehrten Richtung, nämlich keine Methoden nachträglich in ein Interface einfügen zu können, aber das dürfte sich mit Java 8 ja ändern). Das ist zumindest für mich eine Rechtfertigung, möglichst wenig public zu machen (und eine Ausprägung davon ist der o.a. Gedanke der „axiomatischen Interfaces“) - auch wenn ich nicht die Möglichkeit ausschließen kann, dass man es damit übertreiben kann. Eine API die nur noch eine einzige public-Methode doEverything() hat ist wohl nicht so nützlich :smiley:

Und nochmal @Bleiglanz : Gerade in bezug auf Threads sehe ich bei static Methods eher einen Vorteil. Man könnte sie als eine (prozedural angehaucht-antiquierte, sehr spezielle) Form von „Funktionalem Programmieren“ bezeichnen (da man ja i.a. keinen statischen Zustand damit verbindet!), und damit sind sie automatisch thread-safe. (Abgesehen von dem Fall, dass ein übergebenes Objekt verändert wird, und den du ja schon als „unschön“ bezeichnet hast).

[QUOTE=Marco13]@Bleiglanz : Das Argument der Polymorphie hatte ich ja auch schon angesprochen - und, zumindest dachte ich das: Entkräftet. Es macht in bezug auf die Polymorphie keinen Unterschied, ob man
void doSomething() /* Viel Code */ }
schreibt, oder
void doSomething() { callStaticMethodMitVielCode(); } [/QUOTE]

Doch macht es. Und zwar einen ganz gewaltigen. Gugg dir nochmal mein Code von oben an, da wird es sichtbar. Wenn man sich schon nicht auf “Static am besten nie” einlaesst, gilt in jedem Fall “Static Methoden und Vererbung ist ein NO GO!”.

Ob sich der TO wohl schon wundert? Solche Diskussionen zeigen nur, wie schwierig (und ungenügend formuliert) das Problem des OO-Design ist: keiner hat einen blassen Schimmer, welches Vorgehen in welcher Situation das “RICHTIGE” ist. Da gehört schon viel Erfahrung dazu, trotzdem kriegt man manchmal einen Refactoring-Anfall und modelliert seinen Quatsch ganz anders. Vor allem, weil am Ende “das Programm ja läuft” und es in der Arbeit keine Schönheits- und Eleganzpreise zu gewinnen gibt.

Hat glaub ich wenig Sinn da weiterzudiskutieren, weil das Prinzip “static wenn möglich vermeiden und nur gezielt verwenden” ja schon genannt wurde

Wenn man von statischen Methoden aus Instanzmethoden aufruft, gelten die gleichen Regeln, wie wenn man von Instanzmethoden aus Instanzmethoden aufruft. Und dass man Polymorphie auf statische Methoden nicht anwenden kann, ist auch klar. Mir ist nicht klar, was damit ausgedrückt werden soll. Sowas wie

public void sortMyList()
{
    Collections.sort(somePrivateList);
}

ist ja auch OK. Niemand würde auf die Idee kommen, dass daran etwas schlecht/falsch/nicht OO ist, oder gar befürworten, den Code aus der ‚Collections#sort‘ methode dort zu inlinen oder eine eigene Klasse „SortableList“ zu erstellen, die diese Methode auf die eine oder andere Art anbietet. Man kann die sort-Methode überschreiben, wenn man will, und mehr sieht man nach außen hin doch sowieso nicht… :confused:

Ja, ich bin überrascht, wie kontrovers die Antworten ausfallen. Das zeigt, dass meine Frage nicht naiv und die Lösung nicht trivial ist. Das ist doch schonmal was :wink:
Ich habe den Eindruck, dass Ihr alle wisst, wovon Ihr sprecht und daher sicher an allen Standpunkten etwas dran ist - auch wenn ich die Insel noch nicht so überblicke, dass ich das selbst alles im Detail nachvollziehen und verstehen kann. Ich habe aber aus der Diskussion für mich als Anfänger den Schluss gezogen, dass mich die Frage, an welcher Stelle statische Methoden zu umgehen wären, sowohl in stilistischer Hinsicht als auch in der Festigung einer objektorientierten Blickrichtung erstmal weiterbringt. Wenn ich diesbezüglich festeren Boden unter den Füßen habe, werde ich mich dann mit den Pro-Argumenten für Klassenmethoden weiter auseinandersetzen. Insofern hat die Diskussion für mich die erhoffte Klarheit gebracht. Danke vielmals nochmal! :slight_smile: