PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Java: Attributwerte aller Objekte einer Klasse manipulieren


Geldmann3
2018-12-30, 03:44:56
Hallo,

im Folgenden definiere ich eine Java-Klasse, welche einen Soldaten mit dessen Gehalt abbildet:

public class Soldat {

private double gehalt = 1000;

public double getGehalt() {
return gehalt;
}

public void setGehalt(double gehalt) {
this.gehalt = gehalt;
}

}

}

Diese Klasse instanziere ich in meiner Main Klasse mehrmals, verdopple das Gehalt aller erstellten Soldaten und gebe zuletzt das aktuelle Gehalt der einzelnen Soldaten aus.

public class Main {

public static void main(String[] args) {

Soldat Ryan = new Soldat();
Soldat Forrest = new Soldat();
Soldat Tanya = new Soldat();
Soldat Terminator = new Soldat();

//Verdopple das Gehalt aller Soldaten
Ryan.setGehalt(Ryan.getGehalt() * 2);
Forrest.setGehalt(Forrest.getGehalt() * 2);
Tanya.setGehalt(Tanya.getGehalt() * 2);
Terminator.setGehalt(Terminator.getGehalt() * 2);

System.out.print(Ryan.getGehalt() + "\n");
System.out.print(Forrest.getGehalt() + "\n");
System.out.print(Tanya.getGehalt() + "\n");
System.out.print(Terminator.getGehalt() + "\n");


}
}

Aber ist es wirklich nötig jedes Soldatenobjekt einzeln anzusprechen um das Gehalt anzupassen oder gibt es in Java die Möglichkeit zum Beispiel zu sagen

for(dieserSoldat: AlleObjekteVomTypSoldat )
{
dieserSoldat.setGehalt(getGehalt * 2);
}

Ich frage mich einfach nur wie ich das auf einem möglichst smarten Weg mache. Natürlich könnte ich zum Beispiel einen Multiplikator in der getGehalt Methode miteinbeziehen, den ich verändere um das Gehalt aller Soldaten zu verändern.

Genauso stelle ich mir aber gerade die Frage mit der Ausgabe am Ende. Gibt es da nicht eine Abkürzung wie z.b.

for(dieserSoldat: AlleObjekteVomTypSoldat )
{
System.out.print(dieserSoldat.getGehalt() + "\n");
}

Sodass ich nicht jeden einzeln ansprechen muss? Die einzige Möglichkeit dafür, welche mir gerade einfällt ist, die Soldaten von Anfang an in ein Array zu packen und dann das Array mit einer ForEach Schleife abzuklappern. Aber was, wenn man ein sehr komplexes Programm hat und eben aus irgendwelchen Gründen nicht alle in einem Array sind?

josefYY
2018-12-30, 08:31:39
Eventuell wäre die Verwendung einer statischen Variable eine passable Lösung.
Eine statische Variable gilt für alle Instanzen einer Klasse.

Nur macht natürlich eine statische Variable 'gehalt' keinen Sinn, da dann ja alle Soldaten immer das gleiche Gehalt bekommen würden.

Aber du könntest ja einen statische Variable als multiplikator verwenden mit denen das individuelle Gehalt jedes Soldaten multipliziert wird.

public class Soldat {

private static double multi = 1;
private double gehalt = 1000;


public double getGehalt() {
return gehalt * multi;
}

public void setGehalt(double gehalt) {
this.gehalt = gehalt;
}

}

}

Dann noch zwei statische Methoden um den Multiplikator zu manipulieren.
public static void setMulti(double new Multi) ....
public stativ double getMutli() ....

Wenn du nun allgemein den Sold um 10% erhöhen möchtest, erhöhst du lediglich den Multiplikator um 10%.

Monger
2018-12-30, 08:59:19
Fängst du grad mit Datenstrukturen an? ;-)
"was wenn" ist arg hypothetisch. Warum sollte eine große Anwendung unstrukturiert sein?
Also: Normalerweise nimmt man da Listen. Oder etwas, was sich in eine Liste transformieren lässt.

Wenn du "alle Objekte von Typ X" haben willst, wirft das Fragen auf. Alle die in der Runtime gerade leben? Auch von Programmbibliotheken die dir gar nicht gehören? Oder doch nur von einem bestimmten Teil? Wenn ja, wie unterscheidest du die?

Monger
2018-12-30, 10:25:51
Eventuell wäre die Verwendung einer statischen Variable eine passable Lösung.

Das mag pedantisch wirken, aber: Erzähl bitte möglichst niemandem, dass er static benutzen soll. Es ist extrem schwierig das wieder aus den Köpfen rauszukriegen, und es gibt halt abgesehen von der Main Methode eigentlich keinen Fall mehr wo man es benutzen muss.

#44
2018-12-30, 12:08:00
Genauso stelle ich mir aber gerade die Frage mit der Ausgabe am Ende. Gibt es da nicht eine Abkürzung [...] Sodass ich nicht jeden einzeln ansprechen muss? Die einzige Möglichkeit dafür, welche mir gerade einfällt ist, die Soldaten von Anfang an in ein Array zu packen und dann das Array mit einer ForEach Schleife abzuklappern. Aber was, wenn man ein sehr komplexes Programm hat und eben aus irgendwelchen Gründen nicht alle in einem Array sind?
Erstmal: Wie Monger schon bemerkte, sollte man keine Arrays nutzen. Das ist meistens keine gute Idee. Da schmeißt man viele Designvorteile weg, weil man sich Performancevorteile einreden will.


Wie stellst du dir denn die Datenhaltung in einem komplexen Programm vor? Warum soll eine Ecke so eines Programms Soldaten kennen, die die andere gar nicht kennt?
Wie soll sowas benutzt werden? Gehälter können nur bei Offizieren angepasst werden, ausdrucken geht nur bei Mannschaftsdienstgraden?
Beide Listen müssen getrennt gepflegt werden und wenn ich eine dritte Funktion habe, wo man mit beiden arbeiten muss, dann hältst/pflegst (!) du die Daten mehrfach?

Typischerweise hat man an dieser Stelle eine Datenhaltungsschicht, die einem die gewünschten Objekte anhand von Filterkriterien/Querys zurück gibt.
Wenn man also alle Soldaten in der Grundausbildung haben möchte, gibt es dafür entweder eine eigene Methode, oder die Möglichkeit einen Filter zu übergeben.

In deinem Beispiel hast du schlicht keine (saubere) Datenhaltungsschicht. Am ehesten ist die Datenhaltungsschicht noch deine Liste/Collection und die muss dann entsprechend natürlich alle Soldaten enthalten.
Das heißt aber nicht, dass dann überall im Programm mit dieser einen Listeninstanz gearbeitet werden sollte. Wegkapseln und Filtermöglichkeiten drumherum bauen!
Ob das dann am Ende ein RDBMS, eine Objektdatenbank, ein dokumentenorientierter Ansatz oder nur eine auf die Platte serialisierte Liste ist, ist da erst mal sekundär.

Klassenvariablen sind auch aus anderer Sicht problematisch: Was, wenn es glatte 100€ Bonuszahlung für alle gibt? Das lässt sich nicht mit einem Multiplikator abbilden. Plötzlich brauchst du noch eine Variable.
Und schon müllst du dir dein Geschäftsobjekt mit Sonderfällen und Ausnahmen zu (was, wenn dann auch noch gehalts-/dienstgradabhängig gestaffelte Einmalzahlungen kommen :eek:), nur weil du keine Schleife für deinen konkreten Nutzungsfall programmieren wolltest.

Geldmann3
2018-12-30, 14:16:22
Zitat von Monger:
Fängst du grad mit Datenstrukturen an? ;-)
Ja, das könnte man so ausdrücken.

Zitat von Monger:
Wenn du "alle Objekte von Typ X" haben willst, wirft das Fragen auf. Alle die in der Runtime gerade leben? Auch von Programmbibliotheken die dir gar nicht gehören? Oder doch nur von einem bestimmten Teil? Wenn ja, wie unterscheidest du die?
Das stimmt, darüber habe ich noch gar nicht so tiefgehend nachgedacht. Das könnte wahrscheinlich wirklich problematisch sein.

Zitat von #44:
Erstmal: Wie Monger schon bemerkte, sollte man keine Arrays nutzen. Das ist meistens keine gute Idee. Da schmeißt man viele Designvorteile weg, weil man sich Performancevorteile einreden will.

Angenommen ich weiß schon im Voraus genau wie groß das Array sein wird, dann sollte es doch kein Problem sein und nur Vorteile gegen Listen haben, oder? Zum Beispiel habe ich letztens ein Array von JSON Objekten in ein Array aus Javaobjekten eingelesen.

Über eine Datenbank habe ich in diesem Fall noch nicht nachgedacht, da am Ende keine Daten persistiert werden sollen.

Wenn ich das Ganze mit eine Liste lösen möchte, wie sorge ich am elegantesten dafür, dass alle Soldaten in dieser Liste enthalten sind? Das Eintragen in die Liste direkt in den Konstruktor der Klasse?

Exxtreme
2018-12-30, 15:00:07
Wenn man solche Massenoperationen auf viele Datensätze durchführen will dann sind Arrays oder Collections das Mittel der Wahl. Sprich, alle Soldaten, die man verändern will in eine List oder ein Set (wenn man keine Duplikate will) packen und dann in einer Foreach-Schleife durchnudeln. Man hat zwar bissl mehr Schreibaufwand dafür aber hinterher weniger Ärger.

Ach ja, für Geldbeträge würde ich kein double/Double nehmen.

Trap
2018-12-30, 15:29:42
Wenn man das halbwegs brauchbar für realistische Anstellungen modelieren möchte:

Jeder Angestellte (z.B. Soldat) hat eine Liste von { Referenz auf Vertrag, Stufe, Startdatum, Enddatum}

Jeder Vertrag hat eine Liste von { Referenz auf Vertragsbedingungen, Startdatum, Enddatum }

Jede Vertragsbedingung hat Liste von { Stufe, Gehalt }


Im einfachsten Fall ist Gehalt verdoppeln dann: alle haben den gleichen Vertrag, man fügt neue Vertragsbedingungen mit doppeltem Gehalt neu hinzu mit Start heute und setzt die alten Bedingungen auf Enddatum gestern.

Monger
2018-12-30, 17:06:30
Angenommen ich weiß schon im Voraus genau wie groß das Array sein wird, dann sollte es doch kein Problem sein und nur Vorteile gegen Listen haben, oder?

Wenn du in die Richtung gehen willst: Es gibt auch in Java Listen die unveränderlich sind.



Wenn ich das Ganze mit eine Liste lösen möchte, wie sorge ich am elegantesten dafür, dass alle Soldaten in dieser Liste enthalten sind? Das Eintragen in die Liste direkt in den Konstruktor der Klasse?
Ja. Ist allgemein ne gute Idee, Variablen bzw. Felder direkt bei der Konstruktion passend zu initialisieren.

#44
2018-12-30, 17:28:16
Angenommen ich weiß schon im Voraus genau wie groß das Array sein wird, dann sollte es doch kein Problem sein und nur Vorteile gegen Listen haben, oder? Zum Beispiel habe ich letztens ein Array von JSON Objekten in ein Array aus Javaobjekten eingelesen.
Es gibt sicher Anwendungsfälle, wo man abwägen kann - meist eher "lowlevel" Funktionalität.

Andererseits: Welchen Vorteil hast du? Eine ArrayList ist auch nur ein bequemer Wrapper um ein Array herum. Wenn du mit einer ArrayList Performance-/Speicherprobleme hast, wird dich auch ein simples Array nicht retten. Da hast du grundlegendere konzeptionelle Probleme.

Du musst dich beim befüllen nicht um Indizes kümmern.
Du musst dich auch quasi sonst nie um Indizes kümmern - Iterator sei Dank.
Arrays können kein Iterator.remove() nutzen.
Arrays können kein contains().
Du hast mit dem Array nicht die Möglichkeit einfach so zu einer Implementierung zu wechseln - z.B. zu einer, die keine NULL-Werte erlaubt.
Es gibt keine Immutable Arrays.
Wenn es dann doch mal nicht bei der fixen Größe bleibt, darfst du bei Arrays explizit damit herumschlagen.

Für jeden dieser Fälle schreibst du für Arrays 100% überflüssigen Boilerplate - oder stellst dann doch auf ne Collection um.

Und wenn sich mal Arrays in deine Interfaces hineingefressen haben - Glückwunsch. Dann ist dein Code garantiert auch voll mit allen möglichen Hacks um damit umzugehen, weil der nachträgliche Umbau dann doch zu Aufwändig wäre und ein weiterer Hack dann auch nicht mehr stört...

Wir haben das selbst oft genug so gemacht - jetzt, wo ich mich seit einer Weile mit den Auswirkungen herumschlagen muss, ärgere ich mich nur über mich selbst.

Wenn ich das Ganze mit eine Liste lösen möchte, wie sorge ich am elegantesten dafür, dass alle Soldaten in dieser Liste enthalten sind? Das Eintragen in die Liste direkt in den Konstruktor der Klasse?
Wie gesagt - eine kleine Abstraktion für die Datenhaltung. Auch wenn es nicht mehr als ein Wrapper um eine Collection im Speicher ist. Saubere Schnittstellen sind immer wichtig.
Entweder übergibst du dann die Objekte nach dem Konstruktoraufruf direkt dahin (nicht aus dem Konstruktor selbst! SRP) oder baust das in der Art einer Factory direkt mit ein (das ist mmn. aber auch nicht sauber - bricht wieder SRP).
Ich meine - es sollte eh nicht zu viele Stellen geben, an denen neue Soldaten angelegt werden können. Bzw. sollte der selbe Code an den nötigen Stellen wiederverwendet werden...

Monger
2018-12-30, 17:50:59
Über eine Datenbank habe ich in diesem Fall noch nicht nachgedacht, da am Ende keine Daten persistiert werden sollen.

Datenhaltung heißt nicht unbedingt gleich SQL Datenbank. Kann auch ne Excel Tabelle sein, oder ne Textdatei, oder XML, oder JSON. Irgendwas, mit dem du Daten festlegen kannst, ohne dass du dein Programm neu kompilieren musst.

Exxtreme
2018-12-30, 19:36:52
Angenommen ich weiß schon im Voraus genau wie groß das Array sein wird, dann sollte es doch kein Problem sein und nur Vorteile gegen Listen haben, oder? Zum Beispiel habe ich letztens ein Array von JSON Objekten in ein Array aus Javaobjekten eingelesen.

Vorteile eines Arrays gegen eine List sind eine etwas bessere Performance und man fängt sich keine Typlöschung ein. Das sind aber zu 99% keine Hürden, die man irgendwie merkt.


Dafür hat man bei List bzw. Generics mehr Vorteile: Foreach-Schleife, bessere Typsicherheit weil Generics invariant sind und allgemein sehr viel weniger Stress. Weil die meisten Bibliotheken Generics als Parameter nehmen bzw. ausspucken. Sprich, es sind Dinge ala Collections.unmodifiableList() ohne Aufwand möglich, die mit Arrays nicht möglich sind ohne massiv selbst Hand anlegen zu müssen. Auch das Stream-API funktioniert hervorragend mit Collections und nicht so gut mit Arrays.

Gast
2018-12-31, 00:11:29
Der Vorschlag von josefYY in seiner Grundidee ist schon ganz gut: Wenn das jetzt viele Objekte sind, die mitunter öfter modifiziert werden, ist das deutlich sinnvoller den Wert nur wo nötig zu skalieren als immer einzeln anzufassen.

So funktioniert das ja auch z.B. in der 3D-Koordinaten-Programmierung: Du skalierst, rotierst und verschiebst nicht jeden Eckpunkt ständig, sondern gibst der GPU die entsprechende Matrix mit, die das lediglich für den aktuellen Input tut.

Alternative: Mit dem Konstruktor deiner Klasse registrierst du dich bei einem Dispatcher und vergibst am besten eine fortlaufende Nummer für jede Instanz. Wenn du dem Dispatcher von außen ein Event gibt, kannst du all deine Objekte, die am Dispatcher hängen, in eine CSV-Datei zusammenserialisieren. Jetzt setzt du Hadoop2 + Hive auf. In dem Hive-Script weist du an, die CSV-Datei zu laden, führst dann mittels UPDATE-Befehl deine Gehaltserhöhungen durch und schreibst mittels INSERT eine neue CSV-Datei mit den Updates. Dein Program, das natürlich mittels RabbitMQ von außen plattformunabhängig Nachrichten empfangen können sollte, sagst du nun, dass es die CSV-Datei mit den Updates nun deserialisieren soll, anhand der fortlaufenden Nummer kannst du jetzt wieder allen Objekten am Dispatcher sagen, welche Zeile sie holen sollen. Mit Jenkins kannst du das ganz gut asynchron, monitorable implementieren. Das skaliert dann ganz Ok und du musst dich um nichts weiter kümmern.

Korfox
2019-01-02, 14:29:08
Vorteile eines Arrays gegen eine List sind eine etwas bessere Performance und man fängt sich keine Typlöschung ein. Das sind aber zu 99% keine Hürden, die man irgendwie merkt.

Man sollte halt einfach das nutzen, was man braucht.
Es ist sicher nicht verkehrt, wenn man erst Mal lernt, wie eine ArrayList intern überhaupt funktioniert (nämlich genau mit einem Array und dem dynamischen vergrößern, indem das Array in ein neues Array kopiert wird, was dann recht stark bremst).
Ich sehe andererseits aber keine Anforderung, dass die Daten irgendwie sortiert sein müssen und dass Dubletten zugelassen sein müssen.
Es gibt nicht nur Listen, die das Eingangsszenario komfortabler als ein Array abbilden.

#44
2019-01-02, 14:39:21
So funktioniert das ja auch z.B. in der 3D-Koordinaten-Programmierung: Du skalierst, rotierst und verschiebst nicht jeden Eckpunkt ständig, sondern gibst der GPU die entsprechende Matrix mit, die das lediglich für den aktuellen Input tut.
Koordinaten liegen aber auch in einem gemeinsamen Koordinatensystem und haben entsprechend einen gemeinsamen Bezug.

Das gilt für Gehälter so erst mal nicht. Nur weil man es machen kann, heißt noch lange nicht, dass dieser Ansatz der Problemdomäne gerecht wird.

Alternative: Mit dem Konstruktor deiner Klasse registrierst du dich bei einem Dispatcher und vergibst am besten eine fortlaufende Nummer für jede Instanz. Wenn du dem Dispatcher von außen ein Event gibt, kannst du all deine Objekte, die am Dispatcher hängen, in eine CSV-Datei zusammenserialisieren. Jetzt setzt du Hadoop2 + Hive auf. In dem Hive-Script weist du an, die CSV-Datei zu laden, führst dann mittels UPDATE-Befehl deine Gehaltserhöhungen durch und schreibst mittels INSERT eine neue CSV-Datei mit den Updates. Dein Program, das natürlich mittels RabbitMQ von außen plattformunabhängig Nachrichten empfangen können sollte, sagst du nun, dass es die CSV-Datei mit den Updates nun deserialisieren soll, anhand der fortlaufenden Nummer kannst du jetzt wieder allen Objekten am Dispatcher sagen, welche Zeile sie holen sollen. Mit Jenkins kannst du das ganz gut asynchron, monitorable implementieren. Das skaliert dann ganz Ok und du musst dich um nichts weiter kümmern.
Falls das Kritik in die Richtung von "Overengineering" sein soll, kann ich nur erwidern:
Wer sich in seinen Fingerübungen nicht mit sauberem Design auseinandersetzt, sondern die schnellsten Hacks nutzt um die Aufgabe quick&dirty abzuschließen, der braucht auch nicht erwarten, dass er dann in einem realen Projekt etwas anderes als quick&dirty fabrizieren wird.

Korfox
2019-01-02, 20:37:33
Das mag pedantisch wirken, aber: Erzähl bitte möglichst niemandem, dass er static benutzen soll. Es ist extrem schwierig das wieder aus den Köpfen rauszukriegen, und es gibt halt abgesehen von der Main Methode eigentlich keinen Fall mehr wo man es benutzen muss.

Was genau spricht gegen statische Methoden Utility-Klassen?

Auch im gegebenen Fall wäre eine Factory für Soldaten (wie bei Command and Conquer), die diese dann auch in einer Datenschicht registriert, ein durchaus gängiger Ansatz. Die Methode, die die Soldaten erstellt ist dann auch static.
Vorteil wäre: man verpackt alles hinter ein schönes API und kann dann intern jede Menge dreckiges Zeug machen, während der Nutzer der Klassen sich damit nicht einschlafen muss.

Dann hätte man halt seine Soldatenfabrik.erzeugeSoldat(...), welche ein Kaserne.registerSoldat(Soldatenobjekt) macht.
Die Kaserne kann dann ein Set aller Soldaten haben, Fehler werfen, wenn eine Dublette erzeugt werden soll, die Soldaten in anderen Collections nach Attributen wegsortieren und filtern usw. usf.

Der Nutzer macht nur sein createSoldat und kann danach Kaserne.gibtMirAlleOffiziere (oder noch blöder/flexibler Kaserne.gibMirAlle(Clazz.Offizier) machen und da durchiterierem und was auch immer.

Implementiert jetzt Soldat noch Serializable kann dir Kaserne sogar ihre Daten z.b. mit Apache JCS gleich simpel persistieren...

Edith: und das würde ich jetzt nicht alles overengineered bezeichnen... Die MongoDB kann man ja dann bei Bedarf dahinter klemmen.

registrierter Gast
2019-01-02, 20:44:53
Private Methoden kann man static machen. Public Methoden aber bitte nicht. Die lassen sich nämlich nur unschön bis gar nicht mocken. Für Tests sind Mocks aber unverzichtbar.

Baalzamon
2019-01-02, 20:47:34
Ich schmeiss mal Unit-Tests als Gegenargument zu statischen Methoden in den Raum.

Static Methods are Death to Testability (http://misko.hevery.com/2008/12/15/static-methods-are-death-to-testability/)

Tante Edith meint ich war zu langsam. :(

Korfox
2019-01-02, 21:04:08
Hmm... Man kann natürlich auch Instanzen jonglieren und intern dann statische Datensenken nehmen, wenn man weiß, dass da bei der Benutzung nichts durcheinander kommt. Bei den Unit-Tests gebe ich Recht, damit kommt man aber eh in die Bredouille, wenn man z.B. einige Libs von Apache nutzt, da da das von mir beschriebene Prinzip bis ins Letzte verfolgt wird.

Monger
2019-01-02, 22:40:49
Was genau spricht gegen statische Methoden Utility-Klassen?

Schon alleine, dass du sie Utility Klassen nennst...
Gibt so viele Punkte in deinem Post auf die ich gerne eingehen würde, aber was vielleicht am wichtigsten ist: Static ist Gift für den Kopplungsgrad. Schlechte Testbarkeit ist nur ein Nebeneffekt davon, aber der resultierende Code ist auch schnell unnötig aufgeblasen, und schlecht wartbar.
Wenn Libraries das vergeigen ist das ärgerlich, aber noch lange kein Grund den selben Fehler zu wiederholen.

Korfox
2019-01-03, 08:19:36
Auch die Standard Java-Libs gehen immer mehr den Weg (Schau dir alleine java.nio an).
Bei durch Ecore generierten Code ist es auch gängig...

Ich versuche static auch so gut wie möglich zu meiden, aber verteufeln sollte man das auch nicht.
Wie gesagt: Quer durch den Code mit irgendwelchen Instanzen Jonglieren ist auch nicht der Hit.
Würdest du auch Caches und Logger wirklich nicht statisch machen? Das würde ja bedeuten, dass ich mein Logger-Objekt wirklich überall hinschleifen muss (oder am besten noch eine Instanz einer Klasse, die nichts anderes tut, als meine Objekte zu verwalten, die ich eigentlich statisch zugreifen möchte...?)...

#44
2019-01-03, 09:21:20
Wie gesagt: Quer durch den Code mit irgendwelchen Instanzen Jonglieren ist auch nicht der Hit.
Würdest du auch Caches und Logger wirklich nicht statisch machen? Das würde ja bedeuten, dass ich mein Logger-Objekt wirklich überall hinschleifen muss (oder am besten noch eine Instanz einer Klasse, die nichts anderes tut, als meine Objekte zu verwalten, die ich eigentlich statisch zugreifen möchte...?)...
Typischerweise hat man dafür dann Dependency Injection. Oder Factories - das machen ja gerade Logging-Frameworks gerne. (Ist natürlich auch eine statische Abhängigkeit)

Ich bin da aber auch im Zwiespalt. Gerade der Punkt mit den Utilities erschließt sich mir auch nicht ganz.
Nehmen wir sowas wie StringUtils.isEmpty als Beispiel - klar bin ich dann gekoppelt. Aber die Alternative wäre doch kaum, ein Objekt vom Typ eines Interfaces "Stringchecker" zu erzeugen um zu Prüfen ob ein String Null oder leer ist? Und wenn ich den Code "inline" einfüge, habe ich noch höhere Kopplung, bin mmn. nicht besser Testbar und habe auch aufgeblasenen Code.
Damit, dass Java diverse Sprachfeatures fehlen oder ich String nicht erweitern kann möchte ich am Ende doch irgendwie leichtgewichtig umgehen.

Wenn die Utilities dann aber plötzlich keine nebeneffektfreien Funktionen mehr sind, läuft aber sehr wahrscheinlich was falsch.

Korfox
2019-01-03, 09:43:42
Factories sind ja statische Implementierungen. Hatte ich ja oben so beschrieben (mit einer Factory und einer Klasse, die die Datenhaltung macht).

Auch Math ist eine Lib, bei der absolut nachvollziehbar ist, dass die als Utility implementiert ist.

Ich denke, wenn man weiß, wo und wann man es einsetzt ist public/protected static nicht so böse, wie es von manchen verteufelt wird.

EDIT: Bitte nicht falsch verstehen. Ich lerne immer und immer gerne dazu. Aber ich verteidige meine Meinung auch hart ;)

Monger
2019-01-03, 10:22:51
Wie gesagt: Quer durch den Code mit irgendwelchen Instanzen Jonglieren ist auch nicht der Hit.
Würdest du auch Caches und Logger wirklich nicht statisch machen?
Ganz besonders die nicht. Was, wenn du mal den Logger wechseln willst? Willst du dann deine ganze Codebasis durchsuchen? Cross-Cutting concerns haben mitten in der Produktlogik nix zu suchen. Das führt jetzt VIEL zu weit, aber: Das verwalten von Instanzen ist ein ganz eigenes Problemfeld, zum Glück aber ein lösbares. In kleinen Programmen kannst du das von Hand lösen, in größeren nimmst du dann Frameworks. Sowas, zum Beispiel:

http://picocontainer.com/interception.html

Korfox
2019-01-03, 10:32:11
Ganz genau deswegen doch. Ich registriere den Logger bei meiner statischen Logger-Klasse und kann im Quellcode den statischen Aufruf stehen lassen.
Das selbe beim Cache.

Monger
2019-01-03, 10:34:02
Nehmen wir sowas wie StringUtils.isEmpty als Beispiel - klar bin ich dann gekoppelt.
Statische Funktionen sind super, wenn sie seiteneffektfrei sind.
Aber statische Factories sind per Definition eben nicht ohne Seiteneffekte. Sie produzieren "aus dem nichts" mehr Informationen als in sie reingesteckt wurden.

Korfox
2019-01-03, 11:37:50
Ganz besonders die nicht. Was, wenn du mal den Logger wechseln willst? Willst du dann deine ganze Codebasis durchsuchen? Cross-Cutting concerns haben mitten in der Produktlogik nix zu suchen. Das führt jetzt VIEL zu weit, aber: Das verwalten von Instanzen ist ein ganz eigenes Problemfeld, zum Glück aber ein lösbares. In kleinen Programmen kannst du das von Hand lösen, in größeren nimmst du dann Frameworks. Sowas, zum Beispiel:

http://picocontainer.com/interception.html
Jetzt machst du mich endgültig durcheinander.
Um selbst keine static zu nutzen/implementieren verweist du auf ein Framework, das ... u.a. statische Factories implementiert ...?

Monger
2019-01-03, 12:07:03
Jetzt machst du mich endgültig durcheinander.
Um selbst keine static zu nutzen/implementieren verweist du auf ein Framework, das ... u.a. statische Factories implementiert ...?
Die entscheidende Frage ist, wo.
Die ganze Idee von Inversion of Control ist ja, dass du Anwendungskomposition als eigene Aufgabe raus isolierst. Das funktioniert aber nur dann, wenn die Komposition aller Komponenten offen ist, du sie also zusammenstecken kannst wie du willst. Ich vergleich das gerne mit Uhu vs. Lego. Im Ergebnis beides ähnlich, bis zu dem Punkt wo du es wieder auseinander nehmen willst.
In der Composition Root kannst du dann beliebige Schweinereien machen, aber eben nur dort. Das gute an nem (guten) IoC Container ist ja, dass 99% deines Codes davon gar nichts mitkriegen.
Aber wie gesagt: von "wie benutze ich Datenstrukturen?" bis hin zu "wie benutze ich IoC Container?" ist ein bissl arg großer Sprung für diesen Thread.

Wollte nur darauf hinweisen, dass auch und gerade in sehr großen und komplexen Maßstäben Static Factories nicht besonders gesund sind (Ausnahmen bestätigen die Regel). Und gerade weil da so ein langer und steiniger Gedankenweg dahinter liegt, ist es sehr schwierig wenn man ein paar Jahre lang solche Antimuster benutzt hat, und sich dann wieder komplett umgewöhnen muss.
Im Endeffekt werden auch Anfänger über diese Stolpersteine rüber müssen, aber man muss sie ja nicht gleich absichtlich in die falsche Richtung schicken.

Korfox
2019-01-03, 12:24:36
Die Factories sind auch von außen nutzbar.
Im Endeffekt machen die IoC Frameworks genau das, was ich im Kleinen weiter oben beschrieben habe (nur, dass Factory und Datenhaltung oft in der selben Klasse geschieht).

Ectoplasma
2019-01-07, 15:15:26
Leute, könnt ihr mich mal aufklären, was ihr so unter einer statischen Factory versteht? Habt ihr bitte mal ein Beispiel, wie ihr das meint?

Wenn ich mir vorstelle, was damit gemeint sein könnte, wird mir irgendwie etwas mulmig. Eine statische Factory ist ja wohl das Anti-Pattern schlechthin und nicht nur das, es widerspricht in jeder Hinsicht dem Datenkapselungsprinzip und auch jedem semantischen Verständis eines vernünftigen Objekt-Models.

Damit hier keine Mißverständnisse aufkommen, ich habe nichts gegen statische Methoden im Allgemeinen, die können sehr nützlich sein.

Monger
2019-01-07, 16:05:55
Der Klassiker aller statischen Fabriken:

string File.Read(path)


Du gibst ihm einen einfachen Identifier, und die Fabrik gibt dir beliebig komplexe Information zurück, die mit jedem Identifier massiv variiert. Gerade beim Dateisystem gibt es natürlich auch Sicherheitsüberlegungen, warum das gut gekapselt und wenig konfigurierbar sein sollte. Wenn man von I/O abstrahieren will, muss man da halt in aller Regel einen eigenen Adapter schreiben, aber das ist ja dann auch okay.

Korfox
2019-01-08, 07:59:39
Ich hatte ja geschrieben, dass es (auch) bei java.nio stark verwendet wird:
https://docs.oracle.com/javase/7/docs/api/java/nio/file/Paths.html
https://docs.oracle.com/javase/7/docs/api/java/nio/file/Files.html

Weitere prominente Beispiele dürften bei Apache POI die Factories sein - z.B.:
https://poi.apache.org/apidocs/dev/org/apache/poi/ss/usermodel/WorkbookFactory.html

Und um es gleich vollständig zu machen... eine prominente und vielgenutzte (zumindest bei Menschen, die EMF nutzen) Utility-Klasse wäre z.B. EcoreUtil:
http://download.eclipse.org/modeling/emf/emf/javadoc/2.5.0/org/eclipse/emf/ecore/util/EcoreUtil.html

Exxtreme
2019-01-08, 21:20:52
Wenn ich mir vorstelle, was damit gemeint sein könnte, wird mir irgendwie etwas mulmig. Eine statische Factory ist ja wohl das Anti-Pattern schlechthin und nicht nur das, es widerspricht in jeder Hinsicht dem Datenkapselungsprinzip und auch jedem semantischen Verständis eines vernünftigen Objekt-Models.

Damit hier keine Mißverständnisse aufkommen, ich habe nichts gegen statische Methoden im Allgemeinen, die können sehr nützlich sein.
Ein Anti-Pattern ist das nicht unbedingt. Im JDK wird das auch immer mehr eingesetzt. Ich sehe das viel mehr als einen Produktivitätsbooster, den man aber nicht wahllos einsetzen sollte sondern schon vorher überlegen sollte ob das im jeweiligen Kontext evtl. Nachteile hat. Und solange sich ein Objekt auch ohne Factory/Builder erstellen lässt ist das auch bei Mocks meist nicht so das Problem. Eine "silver bullet" gibt es beim Programmieren halt nicht.

Monger
2019-01-08, 22:00:38
Programmiersprachen sind halt nicht kontextfrei. Das selbe Muster kann in einem Fall gut, und in einem anderen schlecht sein. Ich bezog mich explizit auf Business Logik.
Static Factories mitten in der BL: Böse
Static Factories als public API für ein gut abgegrenztes framework: Gut
Schwache Typisierung mit Casterei in der BL: Böse
Schwache Typisierung mit Casterei an der UI: Oft irre praktisch

Gibt natürlich auch Muster die sind eigentlich überall böse.