Statische Funktionen gegen Klassen


8

Nehmen wir an, ich möchte einige Dienstprogrammfunktionen erstellen, um einige grundlegende Berechnungen mit dem BigDecimals durchzuführen. Beispielsweise möchte ich eine Funktion haben, die den Durchschnitt von a berechnet List<BigDecimal>.

Was ist der beste Ansatz? Eine statische Funktion oder eine Utility-Klasse?

public static BigDecimal computeAverage(List<BigDecimal> numbers)

oder

public class BigDecimalUtil

public computeAverage(List<BigDecimal> numbers)

Ein zu berücksichtigender Punkt besteht darin, zu ermitteln, welche Klasse für diese Berechnung verantwortlich ist. Möglicherweise stellen Sie fest, dass diese Funktionalität von anderen Klassen nicht global benötigt wird und daher möglicherweise eine private Methode in einer Klasse ist. Eine weitere zu berücksichtigende Sache sind statische Definitionen und Thread-Sicherheit.
NoChance

Antworten:


9

Ich werde meine C # -Lehnungen wahrscheinlich auf die schlechteste Art und Weise verraten, aber ich würde immer statische Methoden innerhalb von Utility-Klassen empfehlen. Dies gibt Ihnen das Beste aus beiden Welten. Die organisatorischen Vorteile der Klasse durch die Verwendbarkeit von Methoden, auf die zugegriffen wird, ohne dass ein Objekt instanziiert werden muss, um sie zu verwenden. Wenn Ihre Utility-Klasse einen Grund hat, ihr Verhalten gelegentlich zu ändern, sind Sie bereits gut positioniert, indem Sie später Vererbung / Schnittstellen einführen, falls dies erforderlich sein sollte. Wenn Sie möchten, dass Ihr Design etwas portabler ist, lässt sich das Paradigma leichter auf andere OO-Sprachen übertragen.


"Wenn Ihre Utility-Klasse einen Grund hat, ihr Verhalten gelegentlich zu ändern": Ja, aber ich bin in einem Java-Shop und Sie können eine statische Methode nicht ändern, indem Sie die Klasse erweitern.
S4M

@ S4M Wenn sich Ihr Methodenverhalten ändern muss, müssen Sie natürlich das Design überdenken. Wie ich in meiner Antwort sagte, sind Sie jedoch gut positioniert, wenn Sie das Design ändern müssen. Sie können die Methode jederzeit so umgestalten, dass sie nicht statisch ist, wenn das Verhalten erweitert werden muss. Wenn Sie alle Ihre Methoden überall verteilt haben, werden Sie Schwierigkeiten haben, sie zu finden. Wenn die Methode nur von einer einzelnen Klasse benötigt wird, ist es natürlich besser, sie in dieser Klasse zu belassen. In diesem Fall ist das Problem, statisch zu sein oder nicht, irrelevant.
S.Robins

Eigentlich bin ich selbst ein großer Fan von statischen Methoden, aber in meiner Firma bemerkte ich, dass ein leitender Entwickler nicht statische Methoden verwendete, was mich zum Nachdenken brachte. Grundsätzlich würde ich ListBigDecimalUtils.computeAverage (x), während er neue AverageListBigDecimal () ausführen würde. Calcute (x). Ich sehe den Nutzen seines Ansatzes nicht.
S4M

@ S4M Es kann ihm einfach ermöglichen, seinen Methoden Zugriff auf andere nicht statische Methoden innerhalb seiner Klasse zu gewähren. Zugegeben, dies könnten einfach zusätzliche private statische Methoden sein. Wahrscheinlicher ist vielleicht, dass er sicherstellt, dass er sich die Möglichkeit lässt, die Klasse zu erweitern, falls er einen Bedarf für die Zukunft sieht. Vielleicht ist es besser, Sie als Senior-Entwickler nach seinen Überlegungen zu fragen, wenn Sie verstehen möchten, wo er die Vorteile sieht.
S.Robins

@ s4m. Ich denke, einfache nicht-verschiedene statische Klassen sind in Ordnung und vielleicht die beste Wahl für den Moment (schade, dass Java übrigens keine Erweiterungsmethode hat). Aber ein "statsservice" ist auch nicht albern. Ich könnte mir vorstellen, dass zur Laufzeit der Durchschnitt vom Mittelwert zum Median zum getrimmten Mittelwert geändert werden muss. Ich kann mir mathematische Operationen auf Listen von Ints vorstellen, die Entropie erzeugen und einen Dienst für die Testbarkeit erfordern würden.
Nathan Cooper

5

Wenn Sie Dienstprogrammfunktionen ausführen möchten, die durch ein bestimmtes Thema verbunden sind, erstellen Sie eine Klasse für sie, machen Sie sie in dieser Klasse statisch und machen Sie einen Konstruktor dieser Klasse privat, um die Möglichkeit eines versehentlichen Ausgebens einer Instanz auszuschließen. Verwenden Sie also die zweite Variante, aber verstecken Sie den Konstruktor.

public class BigDecimalUtil

public computeAverage(List<BigDecimal> numbers)

private BigDecimalUtil(){super();}

Es gibt noch eine Möglichkeit - keinen Konstruktor zu erstellen und diese Klasse abstrakt zu machen. Auf diese Weise können Sie die Instanz der Klasse jedoch nicht nur außerhalb, sondern auch innerhalb der Klasse verwenden. Ich denke, diese zweite Variante ist zu streng für dich. Wähle dich selbst.


4

Ich stimme @Gangnus zu, dass eine statische "Wrapper" -Klasse sinnvoll ist, wenn einzelne Klassenmethoden immer isoliert verwendet werden, und dies wird oft eine gute Antwort sein.

In einer nicht statischen Klasse mit nicht statischen Methoden kann jedoch ein Wert vorhanden sein, wenn Sie verwandte Berechnungen haben, die wahrscheinlich zusammen verwendet werden. Zum Beispiel bauen ein ListStatsUtilObjekt mit List<BigDecimal>in Konstruktor übergeben, könnte es für den Zugriff bequem sein getStandardDeviation, getVarianceund getAverageder Reihe nach, ohne zurück in das Objekt übergeben. Dies ist möglicherweise eleganter, bietet jedoch möglicherweise auch eine bessere Leistung, insbesondere wenn rechenintensive Vorgänge erforderlich sind, die betriebsübergreifend optimiert werden können, indem der private Status innerhalb des Objekts gemeinsam genutzt wird.

public ListStatsUtil {
    private final List<BigDecimal> numbers;
    public ListStatsUtil(List<BigDecimal> numbers) {
        this.numbers = numbers;
    }
    public BigDecimal getAverage() {
        // return average of this.numbers
    }
    public BigDecimal getStandardDeviation() {
        // return std dev of this.numbers
    }
    public BigDecimal getVariance() {
        // return variance of this.numbers
    }
    public BigDecimal getMax() {
        // return max of this.numbers
    }
    public BigDecimal getMin() {
        // return min of this.numbers
    }
}

Ich habe auf dieser neuen Programmierseite noch keine Erlaubnis, einen Kommentar für @Gangnus zu hinterlassen, daher werde ich ihn hier hinterlassen. Ist das super()im privaten Konstruktor für eine statische Klasse in Java sinnvoll?
Codingoutloud

Ohne Zweifel hat es nur Sinn als Selbstdisziplin für den Fall des zukünftigen radikalen Refactorings. Wir möchten, dass dieser Konstruktor niemals ausgeführt wird.
Gangnus

Ich verstehe nicht Alle diese Funktionen, wie getVariance und dergleichen, nehmen List <BigDecimal> und geben BigDecimal von double zurück. Wie könnten Sie sie in Kette legen? Die Idee, Dienstprogramme zu verketten, ist gut, aber a) nicht für den von Ihnen bereitgestellten Fall. b) nicht in dem Fall, um den es geht. Dies ist der Fall, wenn wir eine neue Klasse mit Dienstprogrammen benötigen . Eine solche Frage taucht also nie auf - die Antwort liegt auf der Hand.
Gangnus

Nicht in einer Kette, nur dass Sie möglicherweise mehrere Vorgänge für eine einzelne Liste wünschen. Die Frage schloss diese Möglichkeit nicht aus.
Codingoutloud

Die Klasse, die ich beschrieben habe, kann solche Methoden leicht definieren. Es gibt nur keine Instanz aus , kann sie aber innerhalb von OK verwenden. In diesem Fall wird der Konstruktor natürlich zu einem funktionierenden, aber wir haben uns darauf vorbereitet, nicht wahr? :-)
Gangnus

1

Ich habe für die Codingoutloud-Antwort gestimmt und möchte sie ein wenig erweitern.

Das Verpacken von Sammlungen in Klassen ist etwas, was ich immer versucht habe - meine Regel lautet "Übergeben Sie niemals eine nackte Sammlung". Der größte Fehler von OO, den ich gesehen habe, ist, wenn Leute Angst haben, Methoden hinzuzufügen, wo sie hingehören. In Ihrem Fall gehören die Methoden zur Sammlung (ganz offensichtlich, wenn Sie darüber nachdenken). Legen Sie sie also dort ab, indem Sie die Sammlung einpacken. Um das Sahnehäubchen auf den Kuchen zu setzen, legen Sie nur die Funktionen offen, die Sie außerhalb Ihrer neuen Klasse benötigen (als Codingoutloud) tat), damit Sie sich Ihre einzelne, kleine Klasse ansehen und leicht verstehen können, was diese Sammlung manipuliert.

Sie müssen nicht jedes mögliche Problem lösen, da es sich nur um Ihren Code handelt. Sie können den Sammlungs-Wrapper genauso einfach bearbeiten wie die anderen Klassen, die mit ihm interagieren. Auf diese Weise können Sie bei Bedarf anderen Code in die Klasse ziehen. Wenn Sie die Auflistung nie verfügbar machen, wird schnell klar, welcher Code zum Wrapper gehört.

Auf diese Weise haben Sie auch die vollständige Kontrolle über die Sammlung, sodass Sie verhindern können, dass sie in einen Zustand versetzt wird, der nicht mit ihrer Funktion übereinstimmt. Wenn beispielsweise kein Eintrag in der Sammlung null sein kann, verhindern Sie einfach, dass jemand eine Null einfügt!)

Der größte Teil des fehlerhaften OO-Codes, den ich je gesehen habe, wurde erstellt, weil jemand statische Dienstprogramme oder Inline-Code geschrieben hat, der zu Objekten gehört, zu denen er keinen Code hinzufügen konnte, anstatt die fehlerhaften Objekte in eigenen Code zu verpacken, den er ändern konnte .


In diesem speziellen Fall würde ich sagen, dass der Durchschnitt eine vernünftige Methode war, um eine Sammlung von Zahlen aufzurufen. Sie könnten also am Ende all Ihren Nummerncontainern neue Namen mit etwas mehr Funktionalität geben. Klingt nach einer schlechten Rendite. Dies ist, was Erweiterungsmethoden in c # sind, in Java stecken Sie mit Statik fest.
Nathan Cooper

Dienstprogramme ohne Geschäftslogik haben oft keine gute OO-Lösung. Das Beste, was Sie tun können, sind statische Klassen und feste Klassen wie Sammlungen. Wenn Sie wirklich ein Allzweck-Dienstprogramm ausführen müssen, können Sie es genauso gut aufgeben wie Sammlungen, Mathe-Dienstprogramme und alle anderen Dienstprogramme. In diesem Fall verwendet er wahrscheinlich nur eine Sammlung für seinen Geschäftscode. Warum also eine allgemeine Lösung implementieren?
Bill K

0

Wenn ich mich nicht irre und Java eine reine OO-Sprache ist, können Sie keine Funktionen haben, ohne sie in eine Klasse einzuschließen.

Ihre Antwort lautet also: beides, eine statische Funktion, die in eine statische Dienstprogrammklasse eingeschlossen ist, ähnlich wie die Math-Klasse.


-2

Wie wäre es mit einer CalcAverageStrategy-Schnittstelle, die injiziert wird, und Sie können dann bei Bedarf Mean / Median / Mode-Implementierungen injizieren lassen.

Jede Implementierung ist einfach zu testen und auch zum Testen leicht zu verspotten.


Dies liest sich eher wie ein Kommentar, siehe Wie man antwortet
Mücke
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.