Ist die Verwendung von *** Helper- oder *** Util-Klassen, die nur statische Methoden enthalten, ein AntiPattern

19

Ich bin oft mit Helfer- oder Util-Klassen in Java oder einer anderen Sprache konfrontiert. Also habe ich mich gefragt, ob dies eine Art Anti-Pattern ist und ob die Existenz solcher Klassen nur ein Mangel an Fehlern im Design und in der Architektur einer Software ist.

Oft werden diese Klassen nur mit statischen Methoden begrenzt, die eine Menge Dinge tun. Aber meistens ist es in der Tat kontextabhängig und staatlich.

Meine Frage ist, was ist Ihre Meinung zu solchen statischen Helfer- / Util-Klassen, da der Vorteil natürlich der schnelle Aufruf ist, der nur den Klassennamen verwendet.

Und auf welcher Abstraktionsebene würden Sie solche Klassen vermeiden?

Meiner Meinung nach sollte das Schlüsselwort "static" nur innerhalb der Deklaration einer Klasse (Java) und nicht für Methoden zulässig sein. Meiner Meinung nach könnte es eine gute Alternative sein, Procuedural- und OO-Paradigmen in Java zu kombinieren und einen Missbrauch des Schlüsselworts zu vermeiden.

Ergänzungen aufgrund der Antworten:

Zunächst denke ich, dass es völlig legal ist, verschiedene Paradigmen zu kombinieren und sogar Laufzeit-interpretierte Skriptsprachen in maschinen- oder vm-kompiliertem Code zu verwenden.

Ich habe die Erfahrung gemacht, dass während des Entwicklungsprozesses eines Projekts solche Helfer und Hilfsmittel oder wie auch immer der Name lautet, immer weiter wachsen und in jeder vergessenen Ecke der Codebasis verwendet werden, die ursprünglich modular und flexibel gestaltet war. Und aufgrund von Zeitmangel, um Überarbeitungen vorzunehmen oder das Design erneut zu überdenken, wird es im Laufe der Zeit nur noch viel schlimmer.

Ich denke, staticsollte aus Java entfernt werden. Besonders jetzt, wo es möglich ist, noch ausgefeiltere funktionale Sprachelemente zu verwenden.

Vielfalt
quelle
Ich denke, wenn der Helfer keine Klasse instanziieren muss, ist das in Ordnung. Das Problem ist, wenn der Helfer eine konkrete Implementierung einer Klasse instanziiert und Sie diese Schnittstelle dann nicht verspotten können. Wenn der Helfer eine Schnittstelle oder eine abstrakte Klasse erhält, denke ich, ist es in Ordnung.
Bobek
Es ist in Ordnung, wenn Sie behaupten, prozedurale Programmierung zu machen. Es ist nicht so, wenn Sie behaupten, objektorientierte Programmierung zu machen.
Spotted
1
@Spotted: und da es keinen geschäftlichen Wert hat zu behaupten, dass Sie ausschließlich objektorientiertes Programmieren betreiben, führen Sie immer gemischte Paradigmenprogrammierungen durch und erledigen Sie die Scheiße.
RemcoGerlich
@RemcoGerlich Genau. Mein Punkt war zu bemerken, dass die Antwort hauptsächlich "philosophisch" ist, bezogen auf welches Programmierparadigma Sie es betrachten.
Spotted

Antworten:

20

Nun, Java hat keine freien Funktionen, so dass Sie gezwungen sind, diese als statische Funktionen in eine Pro-Forma-Klasse zu schreiben. Ein notwendiger Workaround ist niemals ein Anti-Pattern, obwohl ihm möglicherweise die Eleganz fehlt.

Als nächstes ist nichts falsch an freien Funktionen. Tatsächlich reduziert die Verwendung freier Funktionen die Kopplung, da sie statt aller wichtigen Details nur Zugriff auf die öffentliche Schnittstelle haben.

Natürlich mindert die Verwendung von freien / statischen Funktionen in keiner Weise die Gefahren eines veränderlichen, gemeinsam genutzten, insbesondere globalen Zustands.

Deduplizierer
quelle
2
@ TimothyTruckle Entweder sie sind statisch oder sie haben eine überflüssige thisund erfordern, dass etwas entsprechend instanziiert wird
Caleth
6
@ TimothyTruckle Weil es Java ist. Andere Sprachen können freie Funktionen haben, ohne dass sie in eine Klasse eingefügt werden müssen, um zu deklarieren, dass es sich nicht um eine Instanzmethode handelt. Und irgendwann muss es eine enge Kopplung geben. Ob das gut ist oder nicht ist , liegt ganz auf das, was die betreffende Funktion tut .
Nvoigt
5
@TimothyTruckle Auf jeden Fall wäre die Aufrechterhaltung des Zustands der Hauptgrund dafür, "nicht gut" zu sein.
Nvoigt
2
@nvoigt, ich bin der Meinung, dass diese Antwort durch ausdrückliche Angabe dieses Punktes erheblich verbessert würde. Stateful Statics sind schlecht; Staatenlose sind gut. Wirklich ganz einfach.
David Arno
3
@ TimothyTruckle Das ist keine enge Kupplung. Sie beschreiben eine lose Kopplung. Eine enge Kopplung in diesem Zusammenhang würde eine Art gegenseitige Abhängigkeit implizieren. Eine Einwegabhängigkeit erfüllt diese Anforderung nicht.
JimmyJames
18

Statische Hilfsprogramm- oder Hilfsfunktionen sind kein Antimuster, wenn sie bestimmten Richtlinien entsprechen:

  1. Sie sollten frei von Nebenwirkungen sein

  2. Sie werden für anwendungsspezifisches Verhalten verwendet, das nicht zu der Klasse oder den Klassen gehört, für die diese Funktionen ausgeführt werden

  3. Sie benötigen keinen gemeinsamen Status

Häufige Anwendungsfälle:

  • Datumsangaben anwendungsspezifisch formatieren
  • Funktionen, die einen Typ als Eingabe annehmen und einen anderen Typ zurückgeben.

Zum Beispiel habe ich in einer Anwendung an Benutzern gearbeitet, die Journaleinträge für ihre Aktivitäten führen. Sie können ein Follow-up-Datum festlegen und eine Ereigniserinnerung herunterladen. Wir haben eine statische Utility-Klasse erstellt, um einen Journaleintrag aufzunehmen und den Rohtext für eine .ics-Datei zurückzugeben.

Es war kein gemeinsamer Staat erforderlich. Es hat keinen Zustand verändert, und das Erstellen eines iCal-Ereignisses war sicherlich anwendungsspezifisch und gehörte nicht zur Journaleintragsklasse.

Wenn die statischen Funktionen oder Dienstprogrammklassen Nebenwirkungen haben oder den gemeinsamen Status erfordern, würde ich empfehlen, diesen Code neu zu bewerten, da hierdurch eine Kopplung eingeführt wird, die für Komponententests möglicherweise schwierig zu verspotten ist.

Greg Burghardt
quelle
4

Es wird von Ihrer Herangehensweise abhängen. Viele Anwendungen sind funktionaler als die klassische Denkweise (einschließlich eines Großteils der Websites, auf denen Sie sich befinden).

Mit dieser Einstellung gibt es viele Dienstprogrammmethoden für Worker-Klassen, die als statische Methoden aneinandergereiht werden können. Sie werden näher an einfache Objekte gefüttert / verwendet, die nur Daten enthalten und weitergegeben werden.

Es ist ein gültiger Ansatz und kann sehr gut funktionieren, insbesondere im Maßstab.

Tracker1
quelle
"Kann sehr gut funktionieren, besonders im Maßstab" Nein. Die durch statischen Zugriff verursachte enge Kopplung wird Ihnen bald im Wege stehen, wenn das Projekt wächst.
Timothy Truckle
@TimothyTruckle Könnten Sie erklären, wie Agent.Save (obj) so viel enger gekoppelt ist als obj.Save ()? Der Agent kann auch für statische Methoden eine DI / IoC-Referenz abrufen wie eine Klasseninstanz. Als Referenz ist Stack Exchange selbst in dieser Art von Denkweise geschrieben und hat sich mit weniger Ressourcen als jede andere mir bekannte ASP.Net-App besser skaliert.
Tracker1
"Können Sie erklären, wie Agent.Save (obj) so viel enger gekoppelt ist als obj.Save ()?" Das ist das falsche Beispiel. Ein korrektes Beispiel wäre Agent.Save(obj)vs. agentInstance.Save(obj). Mit letzterem haben Sie die Möglichkeit , agentInstance in den Verwendungscode einzufügen. Infolgedessen können Sie jede untergeordnete Klasse von Agentauch injizieren, was die Wiederverwendung des Verwendungscodes mit verschiedenen "Typen" von Agentohne Neukompilierung ermöglicht. Dies ist eine niedrige Kopplung .
Timothy Truckle
Ich denke, es kommt wirklich auf ein paar Dinge an. Ist der Status erforderlich, wie flexibel muss er sein, und ist es in Ordnung, einen globalen / Instanzstatus zu haben? Es liegt an jedem Entwickler / Architekten, zu entscheiden. Ich würde eine einfachere Codebasis der Komplexität von Szenarien vorziehen, die es erschweren, alles als Ganzes zu verstehen. Eines der Dinge, die ich an node / js liebe, ist, dass ich einfachen / sauberen Code schreiben und trotzdem zum Testen injizieren kann, ohne dass der Code in DI / IoC-Spezifika geschrieben ist.
Tracker1
2

Ich persönlich denke, das einzig Wichtige an solchen Helferklassen ist, dass sie privat gemacht werden. Ansonsten sind sie eine gute Idee (sparsam angewendet).

Die Art und Weise, wie ich das sehe, ist, wenn bei der Implementierung einer Klasse (oder Funktion) so etwas hilfreich ist und die Implementierung klarer macht, wie könnte das schlecht sein? Und OFTEN ist es wichtig, solche privaten Helferklassen zu definieren, um die Integration und Verwendung anderer Algorithmen zu ermöglichen, die davon abhängen, dass Daten in einer bestimmten Form vorliegen.

Ob es als "Helfer" bezeichnet wird, ist eine kleine Sache des persönlichen Geschmacks, aber es bedeutet, dass es bei der Implementierung hilft und für ein breiteres Publikum nicht von Interesse / Nutzen ist. Wenn das Sinn macht - machen Sie es!

Lewis Pringle
quelle
1
Öffentliche Versorgungsklassen sind möglicherweise weiterhin hilfreich. Prime Beispiel: java.lang.Math.
Amon
1

Die Verbreitung von staticHelferklassen beruht auf einem Missverständnis. Nur weil wir Klassen nur mit staticMethoden "Dienstprogrammklassen" aufrufen, heißt das nicht, dass es nicht erlaubt ist, allgemeines Verhalten in POJOs zu schreiben.

static Hilfsklassen sind aus drei Gründen Anti-Pattern:

  1. Durch den statischen Zugriff auf diese Hilfsmethode werden Abhängigkeiten ausgeblendet . Wenn diese "Dienstprogrammklassen" POJOs wären, könnten Sie sie als Konstruktorparameter in eine abhängige Klasse einfügen, wodurch die Abhängigkeit für jeden Benutzer einer abhängigen Klasse offensichtlich wird.

  2. Der statische Zugriff auf diese Hilfsmethoden führt zu einer dichten Kopplung . Dies bedeutet, dass der Code mit den Hilfsmethoden nur schwer wiederverwendbar und (als Nebeneffekt) schwer zu testen ist.

  3. Insbesondere wenn sie den Status beibehalten, handelt es sich lediglich um globale Variablen . Und hoffentlich argumentiert niemand, dass globale Variablen gut sind ...

Statische Hilfsklassen sind Teil des STUPID-Code- Anti-Patterns.


Globaler Zustand hat nichts mit der Frage zu tun, - max630

Das OP schrieb:

Aber meistens ist es in der Tat kontextabhängig und staatlich.

Statische Statefull-Konstrukte sind globale Zustände.

Jede Art von Code kann es verwenden. - max630

Ja, natürlich. Und fast alle Anwendungen benötigen einen globalen Status .

Aber globaler Zustand ! = Globale Variable .

Sie können einen globalen Status mit OO-Techniken über die Abhängigkeitsinjektion erstellen. Sie können jedoch einen globalen Status nicht mit statischen Statusstrukturen vermeiden, bei denen es sich im unteren Bereich um globale Variablen handelt .

Timothy Truckle
quelle
2
Der globale Status hat nichts mit der Frage zu tun, jeder Code kann ihn verwenden.
Max630
@ max630 siehe bitte mein Update
Timothy Truckle
1
Sie können statisch freie Funktionen ohne Kopplung verwenden. Sie übergeben Func<Result, Args>Objekte, die an anderer Stelle instanziiert werden.
Caleth
1
1) Ich halte es im Allgemeinen für eine gute Sache, die Implementierung zu verbergen, wenn dies angemessen ist. 2) Wenn Sie alles als Argumente / Abhängigkeitsinjektion haben, entsteht auch eine Art Entwurfszeitkopplung. Sie spüren dies, wenn Sie viel Zeit mit dem Ändern von Signaturen in langen Abhängigkeitsketten verbringen müssen. Und wie jede einzelne Funktion, die Ihren Logger oder andere Querschnittsthemen erfordert, als Argument, urgh ... (Aber ja, es kann das Testen auf niedriger Ebene erschweren) 3) Natürlich sollten statische Funktionen zustandslos sein.
Alex
1
@Alex Dann lassen Sie es mich anders herum sagen: Eine Unit ist ein beliebiger Haufen Code (einschließlich statisch referenzierter "Utility" -Methoden), der den gleichen Grund hat, sich zu ändern . Alles in dieser Einheit ist Implementierungsdetail . Alles andere ist eine Abhängigkeit und die Einheit sollte keinen statischen Zugriff darauf haben.
Timothy Truckle