Ich habe diesen Thread gesehen
Wenn eine "Utilities" -Klasse böse ist, wo lege ich meinen generischen Code ab?
und dachte, warum sind Gebrauchsklassen böse?
Nehmen wir an, ich habe ein Domain-Modell, das Dutzende von Klassen umfasst. Ich muss in der Lage sein, Instanzen xml-ify. Mache ich eine toXml-Methode für das übergeordnete Element? Mache ich eine MyDomainXmlUtility.toXml-Hilfsklasse? Dies ist ein Fall, in dem der Geschäftsbedarf das gesamte Domänenmodell umfasst - gehört er wirklich als Instanzmethode? Was ist, wenn es eine Reihe von Hilfsmethoden für die XML-Funktionalität der Anwendung gibt?
design-patterns
software-design
hvgotcodes
quelle
quelle
Antworten:
Utility-Klassen sind nicht gerade böse, aber sie können die Prinzipien verletzen, die ein gutes objektorientiertes Design ausmachen. In einem guten objektorientierten Design sollten die meisten Klassen eine einzelne Sache und alle ihre Attribute und Operationen darstellen. Wenn Sie an einer Sache arbeiten, sollte diese Methode wahrscheinlich ein Mitglied dieser Sache sein.
Es gibt jedoch Situationen, in denen Sie Dienstprogrammklassen verwenden können, um eine Reihe von Methoden zu gruppieren. Ein Beispiel ist die
java.util.Collections
Klasse, die eine Reihe von Dienstprogrammen bereitstellt, die für jede Java-Sammlung verwendet werden können. Diese sind nicht spezifisch für einen bestimmten Sammlungstyp, sondern implementieren Algorithmen, die für jede Sammlung verwendet werden können.Was Sie wirklich tun müssen, ist über Ihr Design nachzudenken und festzustellen, wo es am sinnvollsten ist, die Methoden zu platzieren. Normalerweise handelt es sich um Operationen innerhalb einer Klasse. Manchmal handelt es sich jedoch tatsächlich um eine Utility-Klasse. Wenn Sie jedoch eine Utility-Klasse verwenden, werfen Sie nicht nur zufällige Methoden hinein, sondern organisieren Sie die Methoden nach Zweck und Funktionalität.
quelle
Ich denke , dass der allgemeine Konsens ist , dass die Utility - Klassen sind nicht böse per se . Sie müssen sie nur mit Bedacht einsetzen:
Entwerfen Sie die statischen Dienstprogrammmethoden so, dass sie allgemein und wiederverwendbar sind. Stellen Sie sicher, dass sie staatenlos sind. dh keine statischen Variablen.
Wenn Sie über viele Dienstprogrammmethoden verfügen, partitionieren Sie diese so in Klassen, dass Entwickler sie leichter finden können.
Verwenden Sie keine Dienstprogrammklassen, bei denen statische oder Instanzmethoden in einer Domänenklasse die bessere Lösung wären. Überlegen Sie beispielsweise, ob Methoden in einer abstrakten Basisklasse oder einer instanziierbaren Hilfsklasse eine bessere Lösung wären.
Ab Java 8 sind "Standardmethoden" in einer Schnittstelle möglicherweise eine bessere Option als Dienstprogrammklassen. (Siehe Schnittstelle Schnittstelle mit Standardmethoden im Vergleich zur Abstract-Klasse in Java 8. Beispiel.)
Die andere Möglichkeit, diese Frage zu betrachten, besteht darin, zu beobachten, dass in der zitierten Frage "Wenn Gebrauchsklassen" böse "sind" ein Strohmann-Argument ist. Es ist so, als würde ich fragen:
In der obigen Frage sage ich eigentlich nicht, dass Schweine fliegen können ... oder dass ich dem Vorschlag zustimme, dass sie fliegen könnten .
Typische "xyz ist böse" Aussagen sind rhetorische Mittel, die Sie zum Nachdenken anregen sollen, indem sie einen extremen Standpunkt vertreten. Sie sind selten (wenn überhaupt) als Aussagen wörtlicher Tatsachen gedacht.
quelle
Dienstprogrammklassen sind problematisch, da sie Verantwortlichkeiten nicht mit den Daten gruppieren können, die sie unterstützen.
Sie sind jedoch äußerst nützlich und ich baue sie die ganze Zeit entweder als dauerhafte Strukturen oder als Sprungbrett während eines gründlicheren Refaktors.
Aus Sicht von Clean Code verstoßen Dienstprogrammklassen gegen die Einzelverantwortung und das Open-Closed-Prinzip. Sie haben viele Gründe zu ändern und sind von Natur aus nicht erweiterbar. Sie sollten eigentlich nur während des Refactorings als Zwischenkruft existieren.
quelle
class
Schlüsselwort verwendet wird. Es quakt wie ein Namespace, es läuft wie ein Namespace - es ist einer ...x.equals(y)
weil 1) die Tatsache, dass dies wirklich keinen Zustand ändern sollte, überhaupt nicht vermittelt wird (und ich kann mich nicht darauf verlassen, dass ich die Implementierung nicht kenne) 2) Ich hatte nie vor,x
einen " bevorzugte "oder" behandelte "Position im Vergleich zuy
3) Ich möchte keine" Identitäten "vonx
oder berücksichtigen,y
sondern bin nur an deren Werten interessiert.Ich nehme an, es wird böse, wenn
1) Es wird zu groß (gruppieren Sie sie in diesem Fall einfach in sinnvolle Kategorien).
2) Methoden, die keine statischen Methoden sein sollten, sind vorhanden
Aber solange diese Bedingungen nicht erfüllt sind, halte ich sie für sehr nützlich.
quelle
Widget.compareTo(Widget widget)
gegenüber der statischenWidgetUtils.compare(Widget widgetOne, Widget widgetTwo)
. Der Vergleich ist ein Beispiel für etwas, das nicht statisch durchgeführt werden sollte.Faustregel
Sie können dieses Problem aus zwei Perspektiven betrachten:
*Util
Methoden sind oft ein Hinweis auf schlechtes Code-Design oder eine faule Namenskonvention.Beispiel 1. Richtige Verwendung von
util
Klassen / Modulen. Beispiel für eine externe BibliothekNehmen wir an, Sie schreiben einen Antrag, der Kredite und Kreditkarten verwaltet. Daten aus diesen Modulen werden über Webdienste im
json
Format verfügbar gemacht . Theoretisch können Sie Objekte manuell in Zeichenfolgen konvertieren, die sich in befindenjson
, aber das würde das Rad neu erfinden. Die richtige Lösung wäre, in beide Module eine externe Bibliothek aufzunehmen, die zum Konvertieren von Java-Objekten in das gewünschte Format verwendet wird. (im Beispielbild habe ich gson gezeigt )Beispiel 2. Richtige Verwendung von
util
Klassen / Modulen. Schreiben Sie Ihre eigenenutil
ohne Ausreden an andere TeammitgliederAls Anwendungsfall wird angenommen, dass wir einige Berechnungen in zwei Anwendungsmodulen durchführen müssen, aber beide müssen wissen, wann es in Polen Feiertage gibt. Theoretisch können Sie diese Berechnungen in Modulen durchführen. Es ist jedoch besser, diese Funktionalität zu extrahieren, um das Modul zu trennen.
Hier ist ein kleines, aber wichtiges Detail. Klasse / Modul, die Sie geschrieben haben, heißt
HolidayUtil
aber nichtPolishHolidayCalculator
. Funktionell ist es eineutil
Klasse, aber wir haben es geschafft, generische Wörter zu vermeiden.quelle
Utility-Klassen sind schlecht, weil sie bedeuten, dass Sie zu faul waren, um sich einen besseren Namen für die Klasse auszudenken :)
Davon abgesehen bin ich faul. Manchmal müssen Sie nur die Arbeit erledigen und Ihre Gedanken sind leer. Dann schleichen sich "Utility" -Klassen ein.
quelle
Wenn ich jetzt auf diese Frage zurückblicke, würde ich sagen, dass C # -Erweiterungsmethoden die Notwendigkeit von Dienstprogrammklassen vollständig zerstören. Aber nicht alle Sprachen haben ein so geniales Konstrukt.
Sie haben auch JavaScript, mit dem Sie dem vorhandenen Objekt einfach eine neue Funktion hinzufügen können.
Aber ich bin mir nicht sicher, ob es wirklich eine elegante Möglichkeit gibt, dieses Problem in einer älteren Sprache wie C ++ zu lösen ...
Guter OO-Code ist schwer zu schreiben und schwer zu finden, da das Schreiben von gutem OO viel mehr Zeit / Wissen erfordert als das Schreiben von anständigem Funktionscode.
Und wenn Sie ein begrenztes Budget haben, ist Ihr Chef nicht immer froh zu sehen, dass Sie den ganzen Tag damit verbracht haben, eine Reihe von Klassen zu schreiben ...
quelle
Ich stimme nicht ganz zu, dass Utility-Klassen böse sind.
Während eine Utility-Klasse in gewisser Weise gegen OO-Principals verstoßen kann, sind sie nicht immer schlecht.
Stellen Sie sich beispielsweise vor, Sie möchten eine Funktion, die eine Zeichenfolge aller Teilzeichenfolgen bereinigt, die dem Wert entsprechen
x
.stl c ++ (ab sofort) unterstützt dies nicht direkt.
Sie können eine polymorphe Erweiterung von erstellen
std::string
.Das Problem ist jedoch, dass JEDER String, den Sie in Ihrem Projekt verwenden, wirklich Ihre erweiterte String-Klasse sein soll.
Es gibt Zeiten, in denen OO nicht wirklich Sinn macht, und dies ist eine davon. Wir möchten, dass unser Programm mit anderen Programmen kompatibel ist, also bleiben wir bei
std::string
einer KlasseStringUtil_
(oder etwas anderem) und erstellen sie .Ich würde sagen, es ist am besten, wenn Sie sich an einen Util pro Klasse halten. Ich würde sagen, es ist albern, einen Util für alle Klassen oder viele Utils für eine Klasse zu haben.
quelle
Es ist sehr einfach, etwas als Dienstprogramm zu kennzeichnen, nur weil der Designer sich keinen geeigneten Ort für die Eingabe des Codes vorstellen konnte. Es gibt oft nur wenige echte "Dienstprogramme".
Als Faustregel behalte ich normalerweise Code in dem Paket, in dem er zuerst verwendet wird, und überarbeite ihn dann nur dann an einen allgemeineren Ort, wenn ich später feststelle, dass er wirklich woanders benötigt wird. Die einzige Ausnahme ist, wenn ich bereits ein Paket habe, das ähnliche / verwandte Funktionen ausführt, und der Code dort am besten passt.
quelle
Dienstprogrammklassen mit zustandslosen statischen Methoden können hilfreich sein. Diese sind oft sehr einfach zu testen.
quelle
Mit Java 8 können Sie statische Methoden in Schnittstellen verwenden ... Problem gelöst.
quelle
Die meisten Util-Klassen sind schlecht, weil:
Es gibt einige Analogien zu statischen und dynamischen Bibliotheken.
quelle
Wenn ich einer Klasse keine Methode hinzufügen kann (z. B.
Account
gegen Änderungen durch Jr. Developers gesperrt ist), füge ich meiner Utilities-Klasse nur einige statische Methoden hinzu:quelle
Account
Datei zu entsperren . Oder besser mit nicht sperrenden Versionsverwaltungssystemen.Account
Datei so gesperrt war, dass Jr. Entwickler keine Änderungen daran vornehmen können. Als Jr. Developer hat er wie oben beschrieben vorgegangen, um das Problem zu umgehen. Das heißt, noch besser, nur Schreibzugriff auf die Datei zu bekommen und es richtig zu machen.Utility-Klassen sind nicht immer böse. Sie sollten jedoch nur die Methoden enthalten, die für eine Vielzahl von Funktionen gelten. Wenn es Methoden gibt, die nur für eine begrenzte Anzahl von Klassen verwendet werden können, sollten Sie eine abstrakte Klasse als gemeinsames übergeordnetes Element erstellen und die Methoden darin ablegen.
quelle