Gibt es Anlass zur Sorge, dass "Util" -Stunden stattfinden? [geschlossen]


14

Manchmal erstelle ich 'Util'-Klassen, die hauptsächlich dazu dienen, Methoden und Werte zu speichern, die anscheinend nicht wirklich woanders hingehören. Aber jedes Mal, wenn ich eine dieser Klassen erstelle, denke ich: "Oh, ich werde es später bereuen ...", weil ich irgendwo gelesen habe, dass es schlecht ist.

Auf der anderen Seite scheint es für sie zwei zwingende (zumindest für mich) Fälle zu geben:

  1. Implementierungsgeheimnisse, die in mehreren Klassen innerhalb eines Pakets verwendet werden
  2. Bereitstellung nützlicher Funktionen zur Erweiterung einer Klasse, ohne deren Schnittstelle zu überladen

Bin ich auf dem Weg zur Zerstörung? Was du sagst !! Soll ich umgestalten?


11
Stoppen Sie die Verwendung Utilin den Namen Ihrer Klassen. Problem gelöst.
Yannis

3
@ MattFenwick Yannis hat einen guten Punkt. Das Benennen einer Klasse SomethingUtilist etwas faul und verschleiert nur den wahren Zweck der Klasse - genau wie bei Klassen mit dem Namen SomethingManageroder SomethingService. Wenn diese Klasse eine einzige Verantwortung hat, sollte es einfach sein, ihr einen aussagekräftigen Namen zu geben. Wenn nicht, ist das das eigentliche Problem ...
MattDavey

1
@MDavey guter Punkt, es war wahrscheinlich eine schlechte Wortwahl Util, obwohl ich offensichtlich nicht erwartet hatte, dass das fixiert und der Rest der Frage ignoriert wird ...

1
Wenn Sie mehrere * Util-Klassen erstellen, die durch die Art des zu bearbeitenden Objekts getrennt sind, ist dies meiner Meinung nach in Ordnung. Ich sehe kein Problem mit einem StringUtil, ListUtil, etc. Wenn Sie nicht in C # sind, sollten Sie Erweiterungsmethoden verwenden.
Marco-Fiset

4
Ich habe oft eine Reihe von Funktionen, die für verwandte Zwecke verwendet werden. Sie lassen sich einer Klasse nicht wirklich zuordnen. Ich brauche keine ImageResizer-Klasse, sondern nur eine ResizeImage-Funktion. Also werde ich verwandte Funktionen in eine statische Klasse wie ImageTools einfügen. Für Funktionen, die noch keiner Gruppierung angehören, gibt es eine statische Util-Klasse, in der sie gespeichert werden können. Sobald jedoch einige Funktionen vorhanden sind, die so miteinander verknüpft sind, dass sie in eine Klasse passen, werden sie verschoben. Ich sehe keinen besseren Weg, um damit umzugehen.
Philip

Antworten:


26

Modernes OO-Design akzeptiert, dass nicht alles ein Objekt ist. Manche Dinge sind Verhaltensweisen oder Formeln, andere haben keinen Zustand. Es ist gut, diese Dinge als reine Funktionen zu modellieren, um die Vorteile dieses Designs zu nutzen.

Java und C # (und andere) erfordern, dass Sie eine Util-Klasse erstellen und durch diesen Rahmen springen, um dies zu tun. Ärgerlich, aber nicht das Ende der Welt; und aus gestalterischer Sicht nicht wirklich störend.


Ja, so dachte ich. Danke für die Antwort!

Einverstanden, obwohl erwähnt werden sollte, dass .NET jetzt als Alternative zu diesem Ansatz Erweiterungsmethoden und Teilklassen anbietet. Wenn Sie diesen Punkt auf die Spitze treiben, erhalten Sie Ruby-Mixins - eine Sprache, für die keine Utility-Klassen erforderlich sind.
Neontapir

2
@neontapir - Mit Ausnahme der Implementierung von Erweiterungsmethoden werden Sie grundsätzlich gezwungen, eine Util-Klasse mit den Erweiterungsmethoden zu
erstellen

Wahr. Es gibt zwei Stellen, an denen Util-Klassen behindern, eine befindet sich in der Klasse selbst und die andere ist die Rufseite. Indem die Methoden so aussehen, als ob sie im erweiterten Objekt vorhanden wären, beheben Erweiterungsmethoden das Problem der Aufrufstelle. Ich stimme zu, es gibt immer noch ein Problem mit der Existenz der Util-Klasse.
Neontapir

16

Sag niemals nie"

Ich denke nicht, dass es notwendigerweise schlecht ist, es ist nur schlecht, wenn man es schlecht macht und es missbraucht.

Wir alle brauchen Werkzeuge und Hilfsprogramme

Für den Anfang benutzen wir alle einige Bibliotheken, die manchmal als fast allgegenwärtig und unverzichtbar angesehen werden. Zum Beispiel in der Java-Welt Google Guava oder einige von Apache Commons ( Apache Commons Lang , Apache Commons Collections , etc ...).

Es besteht also eindeutig ein Bedarf für diese.

Vermeiden Sie Hard-Word, Vervielfältigung und das Einführen von Fehlern

Wenn Sie über diese denken , sind ziemlich einfach ein sehr großer Haufen dieser UtilKlassen , die Sie beschreiben, außer jemand durch den großen Längen ging sie (relativ) Recht zu bekommen, und sie haben schon Zeit - getestet und stark ins Auge geballt durch andere.

Ich würde also sagen, die erste Faustregel beim Schreiben einer UtilKlasse ist, zu überprüfen, ob die UtilKlasse tatsächlich noch nicht existiert.

Das einzige Gegenargument, das ich dafür gesehen habe, ist, wenn Sie Ihre Abhängigkeiten begrenzen möchten, weil:

  • Sie möchten den Speicherbedarf Ihrer Abhängigkeiten begrenzen,
  • oder Sie möchten genau kontrollieren, welche Entwickler verwendet werden dürfen (dies geschieht in obsessiven großen Teams, oder wenn ein bestimmtes Framework dafür bekannt ist, dass die eine oder andere super beschissene Klasse irgendwo absolut zu meiden ist).

Beides kann jedoch gelöst werden , indem Sie die Bibliothek mit ProGuard oder einem gleichwertigen Programm neu packen oder selbst zerlegen (für Maven- Benutzer bietet das Maven-Shade-Plugin einige Filtermuster , um diese in Ihren Build zu integrieren).

Wenn es sich also in einer Bibliothek befindet und mit Ihrem Anwendungsfall übereinstimmt und kein Benchmark etwas anderes angibt, verwenden Sie es. Wenn es ein bisschen von dem abweicht, was Sie tun, erweitern Sie es (wenn möglich) oder erweitern Sie es, oder schreiben Sie es in letzter Instanz neu.

Regeln der Namensgebung

Bisher habe ich sie jedoch so genannt Utilwie Sie. Nennen Sie sie nicht so.

Geben Sie ihnen aussagekräftige Namen. Nehmen Sie Google Guava als (sehr, sehr) gutes Beispiel, und stellen Sie sich vor, dass der com.google.guavaNamespace tatsächlich Ihre utilWurzel ist.

Nennen Sie Ihr Paket utilim schlimmsten Fall, aber nicht die Klassen. Wenn es um StringObjekte und die Manipulation von String-Konstrukten geht, nennen Sie es Stringsnicht StringUtils(Entschuldigung, Apache Commons Lang - ich mag und benutze Sie immer noch!). Wenn es etwas Bestimmtes tut, wähle einen bestimmten Klassennamen (wie Splitteroder Joiner).

Gerätetest

Wenn Sie auf das Schreiben dieser Dienstprogramme zurückgreifen müssen, stellen Sie sicher, dass Sie diese Unit-Tests durchführen. Das Gute an Dienstprogrammen ist, dass es sich in der Regel um eigenständige Komponenten handelt, die bestimmte Eingaben annehmen und bestimmte Ausgaben zurückgeben. Das ist das Konzept. Es gibt also keine Entschuldigung, sie nicht einmal zu testen.

Durch Unit-Tests können Sie auch den API-Vertrag definieren und dokumentieren. Wenn die Tests abbrechen, haben Sie entweder etwas falsch geändert, oder Sie versuchen, den API-Vertrag zu ändern (oder Ihre ursprünglichen Tests waren Mist - lernen Sie daraus und wiederholen Sie sie nicht) .

API-Design

Die Designentscheidungen, die Sie für diese APIs treffen, werden Ihnen möglicherweise noch lange folgen. SplitterSeien Sie also vorsichtig , wenn Sie sich nicht stundenlang mit dem Schreiben eines Klons beschäftigen.

Stellen Sie sich einige Fragen:

  • Gewährleistet Ihre Dienstprogrammmethode eine Klasse für sich oder ist eine statische Methode ausreichend, wenn es sinnvoll ist, Teil einer Gruppe ähnlich nützlicher Methoden zu sein?
  • Benötigen Sie Factory-Methoden , um Objekte zu erstellen und Ihre APIs besser lesbar zu machen?
  • Wo wir gerade von Lesbarkeit sprechen: Benötigen Sie eine Fluent-API , Builder usw.?

Sie möchten, dass diese Tools eine große Bandbreite von Anwendungsfällen abdecken, robust, stabil, gut dokumentiert und nach dem Prinzip der geringsten Überraschung in sich geschlossen sind. Im Idealfall kann jedes Unterpaket Ihrer Utils oder zumindest Ihr gesamtes Util-Paket zur einfachen Wiederverwendung in ein Bundle exportiert werden.

Lernen Sie hier wie gewohnt von Riesen:

Ja, viele davon konzentrieren sich auf Sammlungen und Datenstrukturen, aber sagen Sie mir nicht, dass dies nicht der Ort oder das Ziel ist, an dem Sie die meisten Ihrer Utils normalerweise direkt oder indirekt implementieren.


+1 fürIf deals with String objects and manipulation of string constructs, call it Strings, not StringUtils
Tulains Córdova


Warum würden Sie es Strings anstelle von StringUtil (s) nennen? Saiten klingen für mich wie ein Sammlungsobjekt.
BDRX

@bdrx: für mich wäre ein sammlung objekt entweder StringsCollectionTypeHerewenn du eine konkrete umsetzung willst. Oder einen genaueren Namen, wenn diese Zeichenfolgen im Kontext Ihrer App eine bestimmte Bedeutung haben. Für diesen speziellen Fall verwendet Guava Stringsim Gegensatz zu StringUtilsCommons Lang seine stringbezogenen Helfer . Ich finde es vollkommen akzeptabel, es bedeutet für mich nur, dass die Klassen StringObjekte verarbeiten oder eine Allzweckklasse zum Verwalten von Strings sind.
Haylem

@bdrx: Im Allgemeinen nerven NameUtilsmich Sachen, denn wenn es unter einem klar gekennzeichneten Paketnamen steht, würde ich bereits wissen, dass es sich um eine Utility-Klasse handelt (und wenn nicht, würde ich es schnell herausfinden, wenn ich mir die API anschaue). Es ist so ärgerlich für mich als Menschen Sachen wie erklärt SomethingInterface, ISomethingoder SomethingImpl. Ich war mit solchen Artefakten einigermaßen einverstanden, als ich in C codierte und keine IDEs verwendete. Heute würde ich solche Dinge im Allgemeinen nicht brauchen.
Haylem

8

Util-Klassen sind nicht zusammenhängend und im Allgemeinen schlecht gestaltet, da eine Klasse einen einzigen Änderungsgrund haben muss (Prinzip der einheitlichen Verantwortung).

Doch habe ich „util“ Klassen in dem sehr Java API gesehen, wie: Math, Collectionsund Arrays.

Dies sind in der Tat Dienstprogrammklassen, aber alle ihre Methoden beziehen sich auf ein einziges Thema, eine hat mathematische Operationen, eine Methoden zum Bearbeiten von Sammlungen und die andere zum Bearbeiten von Arrays.

Versuchen Sie nicht, völlig unabhängige Methoden in einer Utility-Klasse zu haben. Wenn dies der Fall ist, können Sie sie wahrscheinlich auch woanders hinstellen, wo sie wirklich hingehören.

Wenn Sie util-Klassen haben müssen, versuchen Sie, diese nach Themen wie Java's Math, Collectionsand zu trennenArrays . Zumindest zeigt es eine Absicht des Designs, auch wenn es sich nur um Namespaces handelt.

Ich jedenfalls vermeide immer Gebrauchsklassen und hatte nie das Bedürfnis , eine zu erstellen.


Danke für die Antwort. Also, auch wenn die util-Klassen paket-privat sind, riechen sie trotzdem nach Design?

Allerdings gehört nicht alles in eine Klasse. Wenn Sie an einer Sprache festhalten, die darauf besteht, dass dies der Fall ist, werden Utils-Klassen durchgeführt.
jk.

1
Nun, Utility-Klassen haben kein Konstruktionsprinzip, mit dem Sie wissen können, wie viele Methoden zu viel sind, da es anfangs keine Kohäsivität gibt. Woher kannst du wissen, wie du aufhören kannst? Nach 30 Methoden? 40? 100 ? OOP-Prinzipien geben Ihnen andererseits eine Vorstellung davon, wann eine Methode nicht zu einer bestimmten Klasse gehört und wann die Klasse zu viel tut. Das nennt man Zusammenhalt. Aber wie ich Ihnen in meiner Antwort sagte, verwendeten genau die Leute, die Java entworfen haben, Dienstprogrammklassen. Das kannst du auch nicht. Ich erstelle keine Utility-Klassen und war nicht in einer Situation, in der etwas nicht zu einer normalen Klasse gehört.
Tulains Córdova

1
Utils-Klassen sind normalerweise keine Klassen, sondern lediglich Namespaces, die eine Reihe von, normalerweise reinen Funktionen enthalten. Reine Funktionen sind so zusammenhängend wie möglich.
jk.

1
@jk Ich meinte Utils ... Übrigens mit Namen wie Mathoder Arrayszeigt zumindest eine Absicht des Designs.
Tulains Córdova

3

Es ist durchaus akzeptabel, Util- Klassen zu haben , obwohl ich den Begriff bevorzuge ClassNameHelper. Die .NET BCL enthält sogar Hilfsklassen. Das Wichtigste, an das Sie sich erinnern sollten, ist, den Zweck der Klassen sowie jede einzelne Hilfsmethode gründlich zu dokumentieren und sie zu einem wartbaren Code von hoher Qualität zu machen.

Und lassen Sie sich nicht von Helferklassen mitreißen.


0

Ich benutze einen zweistufigen Ansatz. Eine "Globals" -Klasse in einem "util" -Paket (Ordner). Damit etwas in eine "Globals" -Klasse oder ein "util" -Paket aufgenommen werden kann, muss es sein:

  1. Einfach,
  2. Unveränderlich,
  3. Nicht abhängig von irgendetwas anderem in Ihrer Anwendung

Beispiele, die diese Tests bestehen:

  • Systemweite Datums- und Dezimalformate
  • Unveränderliche globale Daten wie EARLIEST_YEAR (vom System unterstützt) oder IS_TEST_MODE oder ein wirklich globaler und unveränderlicher Wert (während das System ausgeführt wird).
  • Sehr kleine Hilfsmethoden, die Lücken in Ihrer Sprache, Ihrem Framework oder Toolkit schließen

Hier ist ein Beispiel für eine sehr kleine Hilfsmethode, die vom Rest der Anwendung völlig unabhängig ist:

public static String ordinal(final int i) {
    return (i == 1) ? "1st" :
           (i == 2) ? "2nd" :
           (i == 3) ? "3rd" :
           new StringBuilder().append(i).append("th").toString();
}

Wenn ich mir das ansehe, sehe ich einen Fehler, der besagt, dass 21 "21st", 22 "22nd" usw. sein sollte, aber das ist nicht der springende Punkt.

Wenn eine dieser Hilfsmethoden wächst oder kompliziert wird, sollte sie in eine eigene Klasse im util-Paket verschoben werden. Wenn zwei oder mehr Hilfsklassen im util-Paket miteinander verknüpft sind, sollten sie in ein eigenes Paket verschoben werden. Wenn sich herausstellt, dass eine Konstanten- oder Hilfsmethode mit einem bestimmten Teil Ihrer Anwendung zusammenhängt, sollte sie dorthin verschoben werden.

Sie sollten in der Lage sein zu rechtfertigen, warum es keinen besseren Ort für alles gibt, was Sie in einer Globals-Klasse oder einem Util-Paket festhalten, und Sie sollten es regelmäßig mit den oben genannten Tests reinigen. Ansonsten schaffen Sie nur ein Durcheinander.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.