# Wann Membermethoden, wann statische Utility-Methoden?



## Marco13 (26. Feb 2009)

Hi

Mal wieder eine eher philosophische Frage: Nach welchen Kriterien sollte man entscheiden, ob man eine Methode als "richtige" (Member-) Methode einer Klasse anbietet, oder als statische Utility-Methode?

Einige Sachen sind klar: Wenn die Methode irgendwas mit privaten Members macht, hat man nicht so viele Möglichkeiten: Es ist eine Membermethode. Wenn die Methode 2 Parameter übergeben bekommt, die Typen haben, die _einander_ nicht kennen sollten (man also nicht ohne weiteres die Methode in einen der beiden Typen packen möchte) ist die Entscheidung auch klar: Es ist eine statische Utility-Methode. 

In den ALLERmeisten Fällen könnte man sagen, dass man mit etwas Erfahrung "instinktiv das richtige macht", oder "diese Entscheidung im Gefühl hat". Aber mir ist das ein bißchen zu dünn. Gibt es irgendwelche "harten", formalen Kriterien oder Regeln, nach denen man das entscheiden kann?

Der Anlass für diese Frage ist, dass ich jetzt schon ein paar mal 
someList.shuffl...
eingetippt habe... : Ach nee, das ist ja Collections.shuffle(List)! Konkrete Beispiele erweisen sich zwar für solche philosophischen Prinzipfragen häufig als schädlich, aber ich versuch's mal, in der Hoffnung, dass sich keiner zuuu sehr dran aufhängt: Warum ist zum Beispiel "shuffle" keine Methode von "List"?


----------



## tfa (26. Feb 2009)

Ich würde sagen, die Antwort steht schon fast in der Frage (hast du ja auch schon gegeben).
Membermethode immer dann, 

wenn der Zustand (d.h. die _Member_) von Objekten manipuliert oder darauf zugegriffen werden soll, oder
die Methode über ein Interface, das die Klasse implementieren soll, definiert ist.

Statische Methode, 

wenn sie völlig isoliert von Objekten funktioniert (eine _Utility_-Methode eben), oder
wenn für ein Erzeuger-Entwurfsmuster wie Singleton oder Fabrikmethode global erreichbar sein soll.

Letztendlich kann man eh kein Patentrezept geben. Das Paradebeispiel einer Utitlity-Klasse mit vielen statischen Methoden ist z.B. java.lang.Math. Niemand würde bestreiten, dass das ein sinnvolles Design ist. Wenn man sich die Sache aber genauer ansieht, stellt man fest, dass viele Methoden dieser Klasse doppelt und dreifach vorhanden sind - je nach Datentyp. Es gibt sogar eine praktisch identische Klasse namens StrictMath.
Man könnte es aber auch völlig anders machen, nämlich indem man ein Interface "Math" mit allen Utility-Methoden definiert, und dann verschiedene Implementierungen anbietet: MathDouble, MathFloat, MathLong, MathStrictDouble,... Das wären dann alles zustandslose Singletons.



> Warum ist zum Beispiel "shuffle" keine Methode von "List"?


Sollte es eigentlich sein. Es wäre jedenfalls objektorientierter. Ich denke, die haben das aus praktischen Gründen nicht ins Interface gepackt. Jede Implementierung müsste diese Methode bereitstellen, das Interface wird aufgebläht, etc. In Sprachen mit ausgereifteren OO-Features würde man einfach ein Shuffler-Modul seinen Klassen/Objekten per Mix-In hinzufügen. Aber das geht in Java leider nicht.


----------



## Marco13 (26. Feb 2009)

Hmja, irgendwie ist die Antwort nicht sooo einfach... Der Punkt, dass eine Methode eine Membermethode sein muss, wenn...
_die Methode über ein Interface, das die Klasse implementieren soll, definiert ist._
ist ja nur ein Schein-Punkt: Man könnte eine analoge Frage dafür stellen, wann man die Methode ins Interface mit aufnimmt....

Spezielle Anwendungen statischer Methoden wie z.B. Singleton würde ich jetzt auch mal außen vor lassen - das sind Sonderfälle, wo man garkeine praktikable Alternative hat. 

Der Punkt 
_wenn sie völlig isoliert von Objekten funktioniert (eine Utility-Methode eben), oder
sein soll._
ist aber auch nur bedingt konsequent umzusetzen. Theoretisch könnte man zwar genau DIE (minimale) Menge der Methoden definieren, die nötig sind, damit eine Klasse im algebraischen Sinne _vollständig_ und _abgeschlossen_ ist. Das wäre die Menge der Methoden, mit denen man ALLES machen kann, was man mit der Klasse machen können soll, und NICHT MEHR als das. (Also für die Mathematiker: Die Axiom-Methoden  ). Und theoretisch wäre die "sauberste" Trennung genau diese. Jede Methode, die z.B. nichts anderes macht, als die Methoden der Klasse in einer bestimmten Reihenfolge aufzurufen (wie etwa shuffle) könnte dann als statische Utility-Methode geschrieben werden. D.h. sämtliche "convenience-Methoden" würden aus Klassen rausfallen (oder aus Interfaces: List bräuchte kein "indexOf", und kein "contains" (was unter Verwendung von indexOf ja nur ein Einzeiler ist) - von so luxuriösen Sachen wie "containsAll" oder "retainAll" mal ganz zu schweigen...)

Im umgekehrten Fall (weil du meintest, "shuffle" sollte eine Methode von List sein) muss man sich ja Fragen, ob dann nicht auch sort, binarySearch, und alle andere statischen Methoden aus der Collections-Klasse in List gehören würden....

Der Raum der Möglichkeiten ist ziemlich groß. Das ist einerseits gut, aber wenn ich keine fundierten Kriterien habe, auf Basis derer ich die Entscheidung für die eine oder andere Möglichkeit treffen könnte, ist das eher unbehaglich... Vermutlich wieder eine dieser Dinge, wofür man als Softwareentwickler eben das ganze Geld bekommt.

Abgesehen davon merke ich in letzter Zeit immer häufiger, dass die sprachlichen Möglichkeiten, die gängige Sprachen bieten, nicht ausreichen, um so "ideale" Strukturen zu erschaffen, wie man sie sich abstrakt und theoretisch vorstellen könnte. 
Hm.
It's about what people need. It's time for another revolution


----------



## Ebenius (26. Feb 2009)

Marco13 hat gesagt.:


> Warum ist zum Beispiel "shuffle" keine Methode von "List"?


Ohne die Absicht zu hegen, mich daran aufzuhängen, bleibe ich jetzt bei dem Beispiel: Nehmen wir an, eine Liste sei ein Stapel Karten; das bietet sich bei _shuffle / mischen_ ja an.  Wir nehmen an, es gibt zwei Methoden, Karten zu mischen: Zum einen gibt es die Methode, alle Karten auf den Tisch werfern, hin her zu schieben und dann wieder aufzunehmen. Zum anderen gibt es die Methode des Mischens mit zwei Händen (so wie es halt jeder kennt).

Nun nehmen wir an, dass ich bis zu einer Kartenzahl von 64 "normal" mischen kann, darüber wird das schwer, weil meine Hände ja nicht riesig sind und ich würde in jedem Fall die andere Mischmethode benutzen. Also haben wir jetzt einen Algorithmus, der beide Methoden verbindet.

Wäre der Algorithmus abhängig von der Art des Kartenstapels, dann würden wir die Misch-Methode in den Kartenstapel integrieren. Lassen sich aber alle Arten Kartenstapel mit dem selben generischen Algorithmus mischen, sollten wir den Algorithmus nicht in den Kartenstapel hineinwachsen lassen, sondern außerhalb für alle Kartenstapel verfügbar machen.

Just my $0.02

Ebenius


----------



## Marco13 (26. Feb 2009)

Vermutlich hattest du die andere Antwort noch nicht gesehen, aber in bezug auf dein Beispiel: Soweit ich weiß unterscheidet z.B. Collections.sort auf basis der größe der übergebenen Collection, welcher Algorithmus verwendet wird - daher ist das Beispiel sehr direkt übertragbar. Trotzdem ist mir nicht 100% klar, was du damit sagen wolltest. Der letzte Satz
_Wäre der Algorithmus abhängig von der Art des Kartenstapels, dann würden wir die Misch-Methode in den Kartenstapel integrieren. Lassen sich aber alle Arten Kartenstapel mit dem selben generischen Algorithmus mischen, sollten wir den Algorithmus nicht in den Kartenstapel hineinwachsen lassen, sondern außerhalb für alle Kartenstapel verfügbar machen._
klingt wie ein Plädoyer für die Utility-Methoden. Aber gleichzeitig spielst du auf etwas an, was potentiell eine der deutlichsten Schwächen sein könnte: Eine LinkedList (ohne indizierten Zugiff in konstanter Zeit) will (und sollte!) vielleicht gaaanz anders geshuffelt werden als eine ArrayList (die schnellen indizierten Zugriff anbietet). Eigentlich sind solche Sachen doch Entscheidungen, die am besten die jeweilige Implementierung selbst treffen sollte...!?


----------



## Ebenius (26. Feb 2009)

Ich bin an der Stelle für eine Utility-Methode. Sie funktioniert für alle Listen (lediglich abhängig davon ob die Liste RandomAccess implementiert oder nicht verhält sie sich unterschiedlich) und daher muss sie nicht die Implementierungen belästigen. Natürlich ist das immer auch Geschmacksfrage. 

Ebenius


----------



## Marco13 (26. Feb 2009)

Ja, bei einem Beispiel kann man noch die Pros und Contras abwägen und so - aber irgendwie wären mir allgemeingültigere _Kriterien_ lieber. Das eine Extrem (minimale Menge von "Axiom-Methoden", für alles andere Utility-Methoden) ist zwar theoretisch schön und algebraisch sauber und alles, aber sch....lecht zu benutzen: Man WILL eben immer mal convenience-Methoden direkt in der Klasse - aber zu viel convenience ist inconvenient, wegen der aufgeblasenen API und vielen Doppelimplementierungen (alle containsAll-Implementierungen sehen praktisch gleich aus - und in jeder Klasse flackt eine rum...). 

Alle pauschalen Argumente, die mir einfallen würden, sind irgendwie so halbseiden. Ja, mit Utility-Methoden kann man keine Polymorphie mehr verwenden - aber dann doch wieder, weil die unterschiedlichen Implementierungen ja nur die Arbeit an unterschiedliche Utility-Methoden weiterdelegieren könnten. Ja, mit Membermethoden schreibt man Sachen doppelt - aber sie sind praktisch, bequem und intuitiv zu verwenden, und könnten optimierte Impementierungen für die jeweilige Klasse enthalten. 

Es ist wohl mal wieder einer dieser Fälle, wo es (wie tfa schon sagte) keine Patentlösung gibt - und bedauerlicherweise ist das teilweise auf sprachliche Schranken zurückzuführen. (In C++ gibt's Traits, was diesem Dilemma zumindest teilweise entgegenwirkt, aber ... nicht nur bei den Problemen, die man mit einer Sprache zu lösen versucht, gibt es selten eine Patentlösung, sondern schon bei der zu verwendenden Sprache selbst... Vielleicht sollte ich mir mal sowas wie Eiffel oder Smalltalk näher ansehen...)


----------



## schalentier (26. Feb 2009)

Ebenius hat gesagt.:


> Ich bin an der Stelle für eine Utility-Methode. Sie funktioniert für alle Listen (lediglich abhängig davon ob die Liste RandomAccess implementiert oder nicht verhält sie sich unterschiedlich) und daher muss sie nicht die Implementierungen belästigen.



Waere es nicht aber viel objektorientierter, wenn die beiden Sortieralgorithmen an den jeweiligen Basisklassen implementiert waeren? 

Ich versuch statische Methoden wann immer moeglich zu vermeiden. Sie haben einfach zu viele Nachteile (keine Vererbung moeglich, nicht zustandsbehaftet, schwierig zu finden (man muss die Utilklasse kennen), ...).

Math wuerde ich als Ausnahme durchgehen lassen , aber ich frage mich schon, ob ein 
	
	
	
	





```
new Math();
```
 wirklich viel schlimmer waere? Man bedenke die Moeglichkeiten  z.B.: 
	
	
	
	





```
new DegMath();
new RadMath();
```


----------



## ice-breaker (26. Feb 2009)

Marco13 hat gesagt.:


> Soweit ich weiß unterscheidet z.B. Collections.sort auf basis der größe der übergebenen Collection


ja, das hat mit der Komplexität von Algorithmen zu tun, untere und obere Schranke.
Ein Quicksort auf eine kleine Menge von Daten ist langsamer als sonst ein anderer Algorithmus, ist aber schneller als alle anderen ab einer gewissen Schranke.



Marco13 hat gesagt.:


> Aber gleichzeitig spielst du auf etwas an, was potentiell eine der deutlichsten Schwächen sein könnte: Eine LinkedList (ohne indizierten Zugiff in konstanter Zeit) will (und sollte!) vielleicht gaaanz anders geshuffelt werden als eine ArrayList (die schnellen indizierten Zugriff anbietet). Eigentlich sind solche Sachen doch Entscheidungen, die am besten die jeweilige Implementierung selbst treffen sollte...!?


jup, und dann würde man die Shuffle-Methode in die Implementationen der List-Klasse bauen, aber es gibt eben keine besonderen Algorithmen für LinkedLists, da wir auf die keinen wahlfreien Zugriff haben, gibt es da nicht gerade effiziente Methoden dafür, also zu nem Array machen und da weiter behandeln.
Um da aber keine Code-Duplzierungen zu haben müsste man die ganzen Collection-Klasse wieder mit Abstrakten Klassen weiter nach wahlfreien und nicht wahlfreien Methoden aufsplitten für eine Methode, nicht so supi oder?


----------



## Marco13 (26. Feb 2009)

@schalentier: "Wann immmer möglich vermeiden" wäre ja eine schön pauschale Regel - aber auch DAS hat die genannten Nachteile (insbesondere die Potentiellen Doppelimplmentierungen).

@ice-breaker: Ja, dessen bin ich mir bewußt (hatte auch schonmal den Quellcode durchstöbert). Dieser Implementierungsunterscheidungen finde ich irgendwie ziemlich suspekt - als ich das erste mal über http://java.sun.com/javase/6/docs/api/java/util/RandomAccess.html gestolpert bin, hat das einiges an Stirnrunzeln verursacht ... (wenn ich mich genötigt sehen würde, so etwas einzuführen, würde ich mein Design gewaltig in Frage stellen....).

Wie auch immer.Manchmal kann ja der angedeutete Ansatz sinnvoll sein, von den Member-Methoden aus an die Utility-Methoden weiterzudelegieren... so als Mittelweg...


----------



## Ebenius (27. Feb 2009)

schalentier hat gesagt.:


> Waere es nicht aber viel objektorientierter, wenn die beiden Sortieralgorithmen an den jeweiligen Basisklassen implementiert waeren?


Wäre es nicht aber viel netter, keine Suggestivfragen zu stellen? 

Wie ich schon sagte: Geschmackssache.

Ebenius


----------



## schalentier (27. Feb 2009)

Ich versteh das Problem irgendwie ueberhaupt nicht. Koennte mal jemand ein konkretes Beispiel bringen, wo man unbedingt statische Methoden braucht, um doppelten Code zu vermeiden? 

Oben wurde containsAll angebracht, welches immer gleich aussieht...?? Wieso gibts dieses dann nicht irgendwo ganz weit oben in der Hierarchie, genau einmal implementiert? 

Selbst wenn das Sortieren ansich nicht direkt implementiert werden soll - dann benutzt Collection.sort() eben eine entsprechende Sortierklasse:
	
	
	
	





```
public void sort() {
  Sorter sorter;
  if( getSize()>50 ) { sorter = new QuickSort( this ); }
  else { sorter = new BubbleSort( this ); }
  sorter.sort();
}
```

Warum statische Methoden?


----------



## didjitalist (27. Feb 2009)

wie schon mehrfach erwähnt, bleibt es geschmacksfrage. allerdings macht es grundsätzlich fast immer sinn, fachliche aufgaben in gesonderte klassen auszulagern. warum sollte z.b. das List interface eine sort methode anbieten, wenn man in den meisten fällen die liste doch gar nicht sortieren will? und was ist, wenn die implementierung grad zufällig nicht modifizierbar ist? dann hängt da eine sort methode rum, die gar nichts leisten kann und eine exception schmeissen müsste.

wenn man container und manipulatoren immer schön sauber trennt, erhält man auch immer übersichtlich kleine funktionseinheiten. schmeisst man alles zusammen, hat man irgendwann einen unübersichtlichen, unwartbaren moloch. mal eben das sortierverfahren austauschen? denkste, 50 implementierungen haben sich bereits darauf verlassen, dass ihre "sort" methode einfach sortieren soll und sie aus was für gründen auch immer überschrieben.


----------



## tfa (27. Feb 2009)

Marco13 hat gesagt.:


> [...]
> Im umgekehrten Fall (weil du meintest, "shuffle" sollte eine Methode von List sein) muss man sich ja Fragen, ob dann nicht auch sort, binarySearch, und alle andere statischen Methoden aus der Collections-Klasse in List gehören würden...


Könnte man machen. Vielleicht nicht gerade _binarySearch_, sondern nur _search_. Wenn die Liste weiß, dass sie sortiert ist, kann sie ja einen binären Suchalgorithmus nehmen. Der Nachteil ist eben, dass sich das Interface aufbläht.



> Abgesehen davon merke ich in letzter Zeit immer häufiger, dass die sprachlichen Möglichkeiten, die gängige Sprachen bieten, nicht ausreichen, um so "ideale" Strukturen zu erschaffen, wie man sie sich abstrakt und theoretisch vorstellen könnte.



Hast du dir schonmal die moderneren OO-Skriptsprachen angesehen? Damit sind solche Probleme objektorientiert sehr elegant lösbar. Dynamische Typisierung, Duck-Typing (dadurch keine Interfaces), Mix-Ins sind sehr mächtig, aber auch nicht jedermanns Sache. Angeschautz haben sollte man sich es meiner Meinung aber.


----------



## Marco13 (27. Feb 2009)

@schalentier: OK, hatte nicht im Kopf, dass die in AbstractCollection liegt. Die Frage, wo man solche Methoden "unbedingt" braucht, ist ein bißchen heikel: Man braucht sie vielleicht nie "unbedingt".

Aber vielleicht ist genau das eine Form der Frage: Wenn man sie nicht unbedingt braucht, warum gibt es sie dann?  

Die Hauptfrage, die sich mir stellte, war eben, nach welchen Kriterien diese Unterscheidungen getroffen wurden:
Warum liegt "containsAll" in Collection, aber "max" in Collection*S*?
Es könnte auch umgekehrt sein, oder beides in jeweils einer Klasse liegen... Warum ist es genau SO und nicht anders? Und wenn man eigene Klassen schreibt, wie entscheidet man, ob man eine Methode als Member oder Utility-Methode anbietet? 


(Leicht OT @tfa: Ja, insbesondere solche Sachen wie Duck-Typing eröffnen eben ungeahnte Möglichkeiten - aber ggf. auf Kosten von Sicherheit und Effizienz ... es gibt eben kein silver bullet...)


----------



## maki (27. Feb 2009)

Wenn ich zB. einen anderen sort Algo verwenden will, müsste ich von der Klasse erben und diese Methode überschreiben falls es sich um eine Objektmethode handelt, im Falle einer Klassenmethode wird es schwierig..
Wenn sort dagegen in einer Utilityklasse liegt und ich sie direkt aufrufe, kann ich einfach meine eigene utility Methode zum sortieren verwenden.


----------



## Spacerat (27. Feb 2009)

Vllt. hilft ja das Beispiel JOGL... (OpenGL Anbindung an Java, für jene die es nicht kennen). Dort gibt es das Interface "GL", in welchem sich alle unterstützten GL-Methoden und GL-Konstanten wiederfinden. Obwohl die Methoden gut hätten statisch sein können (wie man es z.B. vom GL aus C++ gewohnt ist) sind es in JOGL jedoch allesamt Member der Klasse GL (zwangsläufig, weil Interface). Und obwohl sämtliche Aufrufe einer jeden Instanz bei ein und derselben nativen Methode landen muss man stets
	
	
	
	





```
// Java...
gl.glLoadIdentity();
```
statt blos
	
	
	
	





```
// C++
glLoadIdentity();
```
schreiben. Deswegen halte ich hier (sowie bei "java.lang.Math" auch) Implementationen als statische Utility-Methoden durchaus sinnvoll.


----------



## Marco13 (27. Feb 2009)

@maki: Hmja, der Punkt mit der Vererbung wurde schon genannt, aber es ist IMHO weder ein Pro noch ein Contra für das eine oder andere: Es kann ja praktisch sein, wenn man eine List z.B. mit zwei verschiedenen Algorithmen sortieren will: Einmal ruft man Quicksort.sort(list) auf, und einmal BubbleSort.sort(list). Wenn die Liste "sort" direkt anbietet, kann man im nachhinein nicht mehr beeinflussen, wie die Liste sortiert wird. Selbst wenn man von der Klasse erbt, und "sort" wie gewünscht überschreibt, ist man danach auf die neue Implementierung festgelegt. 

Wenn - im umgekehrten Fall - dieses Verhalten (eben Polymorphie) aber gewünscht ist, muss man eine Membermethode "sort" anbieten. 

Die Lösung, in diesen Methoden dann nur an die passende statische Methoden zu delegieren, gewinnt für mich im Moment immer mehr an Reiz:
[HIGHLIGHT="Java"]
// Statische Utility-Methoden
class BubbleSort { static void sort(List list) { ... } }
class QuickSort  { static void sort(List list) { ... } }

// Interface mit der Member-Methode
interface List { void sort(); }

// Implementierungen, bei denen die Member-Methode
// jeweils unterschiedlich implementiert ist, und
// nur an die passende Utility-Methode delegiert
class LinkedList implements List { void sort() { BubbleSort.sort(this); } }
class ArrayList  implements List { void sort() {  QuickSort.sort(this); } }

// Für Polymorphie: Überschreiben der Member-Methode 
// so dass sie an die passende Utility-Methode delegiert
class SpecialList extends LinkedList
{
    @Override
    void sort()
    {
        SpecialSort.sort(this);
    }
}
[/HIGHLIGHT]
Wobei das vielleicht nicht das beste Beispiel ist ... In so einer Konstellation könnte man auch ein interface "Sorter" mit Implementierungen "QuickSorter" und "BubbleSorter" haben, und alle Listen haben eine Instanz von "Sorter", und wenn man ein anderes Verhalten haben will überschreibt man die "createSorter"-Methode der Liste oder setzt zur Laufzeit einen neuen Sorter oder so...

Möglichkeiten über Möglichkeiten... Schade, dass ich die nehmen muss, die ich in der jeweiligen Situation "am besten finde", und nicht die nehmen kann, von der ich WEISS, dass sie die beste IST ...........


----------



## Ebenius (27. Feb 2009)

Marco13 hat gesagt.:


> @maki: Hmja, der Punkt mit der Vererbung wurde schon genannt, aber es ist IMHO weder ein Pro noch ein Contra für das eine oder andere: Es kann ja praktisch sein, wenn man eine List z.B. mit zwei verschiedenen Algorithmen sortieren will: Einmal ruft man Quicksort.sort(list) auf, und einmal BubbleSort.sort(list). Wenn die Liste "sort" direkt anbietet, kann man im nachhinein nicht mehr beeinflussen, wie die Liste sortiert wird.


Der springende Punkt hierbei ist, dass die optimale Methode des Sortierens eben nicht hauptsächlich von der Listenimplementation (Linked, Array, ...) abhängig ist, sondern eher von den Daten die sich in der Liste befinden. Warum sollte also die Listenimplementation die unabhängig von der Art der Elemente besteht den Sortieralgorithmus mitbringen, wenn sie gar nicht wissen kann, welcher optimal ist.

Ebenius


----------



## Spacerat (27. Feb 2009)

Wenn man dieses Thema so verfolgt, wird die Antwort auf die Frage, wann was Sinnvoll ist eigentlich schon recht Klar. "static" kann man verwenden (und sollte man ggf. auch), wenn die dahinterstehende Methode stets dieselbe ist. z.B. würde kein Mensch einen anderen Weg zur Berechnung des Sinus eines Winkels, als in der Klasse "java.lang.Math" (mal unabhängig davon, das eine "native" Methode hinter dem Beispiel steckt) verwenden. Sortiervorgänge gibt es aber der Anzahl viele. Deswegen wäre hier auch eine Implementation als Member-Methode sinvoll (im Gegensatz zu "Collections.sort()", was damit recht unsinnig wäre, jedoch im kleineren Stil durchaus willkommen ist.).
@Edit: Ebenius, unter welchen Umständen kann eine Listenimplementation nicht wissen, wie sie am besten sortiert wird? Doch nur wenn der Entwickler es nicht weis... oder nicht?


----------



## Ebenius (27. Feb 2009)

Spacerat hat gesagt.:


> Ebenius, unter welchen Umständen kann eine Listenimplementation nicht wissen, wie sie am besten sortiert wird? Doch nur wenn der Entwickler es nicht weis... oder nicht?


Die Wahl des Sortieralgorithmus hängt doch immer von der Art der Daten ab, die sich in der Liste befindet. Wenn meine Daten beispielsweise bereits vorsortiert sind, dann wähle ich einen anderen Sortieralgorithmus als wenn alles kreuz und quer durcheinander gewürfelt ist. Wenn die Daten in der Liste bzgl. der Sortierregeln (Comparable, Comparator) distinkt sind, kann ich einen ggf. schnelleren nicht-stabilen Algorithmus wählen. Es hängt nicht von der Listenimplementierung sondern von der Anwendung der Liste ab, welcher Sortieralgorithmus der optimale ist. Und darüber hat die Implementation der Liste keine Kenntnis. Deshalb wähle ich als Anwender den Algorithmus.

Ebenius


----------



## schalentier (27. Feb 2009)

Alles was ihr sagt seh ich genauso - bis auf den Punkt, warum statische Methoden anstatt Klassen mit richtigen Methoden nehmen?

Wenn das Sortieren von den Daten abhaengt, warum dann nicht eine Klassenhierarchie, die genau das beschreibt? Warum statische Methoden?

Auch der Sinus kann auf zwei verschiedenen Wegen implementiert werden: rad/deg.

Jo, und bloss weil es ein Spachfeature gibt, muss man das nicht unbedingt verwenden... (siehe goto und labels) 

Zum Austauschen der Implementierung: Wie bitte willst du Collections.sort()-Aufrufe austauschen? Ohne alle aufrufenden Stellen zu aendern? Genau das geht doch nur, wenn du Interfaces/Abstrakte Klassen benutzt. Die Loesung, eine polymorphe sort-Methode zu benutzen, um dann statische Methoden an Utitlity-Klassen aufzurufen, halte ich persoenlich fuer ziemlichen Unfug. Warum keine Klasse zum Sortieren nehmen?


----------



## Spacerat (27. Feb 2009)

Eben... mit dem "sort"-Ding stimme ich mit schalentier 100%-ig überein. Selbst der Grund verschiedene Algos zu unterschiedlichen "Sortstadien" der Liste zu verwenden, wäre doch in einer Instanz der Liste besser aufgehoben, weil letztendlich nur die Liste ihren augenbicklichen Sort-Zustand kennt und deswegen den besten Algo auch selbst bestimmen kann, als einen übergebenen Algo verwenden zu müssen, der überhaupt nicht passt.
Über eine 2. Implementierung von "Math.sin()" kann man aber unendlich streiten. Obwohl... Ich habe mir Aufgrund einiger fehlenden Methoden in "Math" inzwischen eine eigene gebastelt, die die Methoden von "java.lang.Math" rekursiv aufruft. Für die Geschichte mit Degrees->Radiands habe ich eine Enummeration (Angle) hinzugefügt, die DEG, RAD und GRAD (damit wären es sogar drei Wege) inklusive Umrechnungsfaktoren beinhaltet. Nun kann man "Math.sin()" konventionell oder mit dem zusätzlichen Parameter Angle aufrufen.


----------



## Marco13 (28. Feb 2009)

schalentier hat gesagt.:


> Alles was ihr sagt seh ich genauso - bis auf den Punkt, warum statische Methoden anstatt Klassen mit richtigen Methoden nehmen?
> ...
> Die Loesung, eine polymorphe sort-Methode zu benutzen, um dann statische Methoden an Utitlity-Klassen aufzurufen, halte ich persoenlich fuer ziemlichen Unfug. Warum keine Klasse zum Sortieren nehmen?



Das Beispiel mit dem "sort" war ein bißchen konstruiert, und ich hatte ja auch erwähnt, dass man das nicht genau so machen würde. 

Die Antwort auf die Frage, warum man für eine einzelne "sort(List)" Methode nicht unbedingt eine Klasse nehmen sollte, ist ähnlich zu der Antwort auf die erste Frage. Die Antwort ist eine Gegenfrage, die lautet: Warum sollte man für eine (potentiell statische) Methode - also eine Methode, die NUR einen Prozess beschreibt - ein eigenes Objekt erstellen müssen? Anders formuliert: Wenn ich ein Objekt erstellen muss, das keinen Zustand hat, nur um eine Methode aufrufen zu können, dann könnte diese Methode auch statisch sein... Dass dabei die Polymorphie verloren geht, wurde ja schon gesagt, aber dafür muss man sich fragen, wer wo und wann und wie man ein Objekt dieser Klasse erstellt (und wer entscheidet, welche genaue Implementierung dieser Klasse dort instantiiert wird) - was in gewissem Sinne (!!!) der Frage entspricht, auf welcher Klasse welche statische Funktion aufgerufen wird... (und, wenn man es darauf anlegt, ist eine statische Methode leicht in ein Objekt einzuwickeln, aber nicht unbedingt umgekehrt...)

Es wurden ja schon ein paar Sachen besprochen, die nur indirekt mit meiner eigentlichen Frage zu tun hatten, oder zu speziell (in bezug auf die allgemeine Form der Frage) waren. Bei jedem speziellen Fall kann man die Argumente abwägen. Ganz allgemein könnte man aber sagen, dass statische Methoden zumindest leicht wiederverwendbar sind, und (was vielleicht noch wichtiger ist) keinen Einfluß auf die Schnittstelle irgendeiner Klasse haben - daher haben sie zumindest aus meiner Sicht eine echte Existenzberechtigung. 
Das einzige wirkliche Gegenargument war ja bisher, dass man die Funktion nicht direkt Polymorph aufrufen kann. Es gibt eben Fälle, wo polymophe Aufrufe keinen Sinn machen, und wenn man doch nachträglich(!) feststellt, dass man sie unbedingt braucht, kann man sie leicht mit statischen Methoden (als building Blocks) nachbauen - ohne wirklichen Implementierungsaufwand.


----------



## Marco13 (29. Mai 2011)

Ein kleiner Nachtrag hierzu: Durch die Defender Methods (PDF Datei) ist das Beispiel, das ich hier beschrieben hatte, direkt durch die Sprache unterstützt. Eigentlich für default-Implementierungen von Interface-Methoden, die später hinzugefügt werden, aber vielleicht macht das die Antwort auf die Titelfrage ja in machen Fällen leichter


----------

