Gibt es einen guten Grund, reine Funktionen nicht öffentlich zu machen?

25

Ich hatte eine kleine Debatte mit einem Kollegen. Gibt es einen guten Grund, reine Funktionen zu verbergen / zu kapseln?

Mit "rein" meine ich die Wikipedia-Definition :

  • Gibt immer die gleichen Ergebnisse von der gleichen Eingabe zurück. (Aus Gründen dieser Diskussion Foo Create(){ return new Foo(); }wird es als unrein angesehen, wenn Fooes keine Wertsemantik gibt.)
  • Verwendet keinen veränderlichen Status (außer lokale Variablen) oder E / A.
  • Erzeugt keine Nebenwirkungen.
Telastyn
quelle
26
Es könnte ein Argument dafür vorliegen, eine solche Funktion überhaupt nicht zu einem Mitglied einer Klasse zu machen.
Pieter B
6
@PieterB - in der Tat, obwohl nicht alle Sprachen dies unterstützen, und einige Sprachen erlauben modulinterne sogar für freie Funktionen.
Telastyn
3
Was war deine Meinung? Ich glaube nicht, dass die Reinheit der Funktion damit zusammenhängt, ob sie in die öffentliche API gehört.
Andres F.
@andresF. - Die Debatte drehte sich tatsächlich um die Frage, ob eine ungebundene Validierungsregel veröffentlicht werden sollte. Ich machte das Argument, da es sich um eine reine Funktion handelte, gab es wenig Schaden. In diesem speziellen Fall wurde die Testbarkeit verbessert und es war wahrscheinlich, dass sie wiederverwendet wird. Bei dieser Frage geht es eher darum, wie weit das Argument gehen könnte / sollte.
Telastyn
2
@Telastyn Wenn es wiederverwendbar ist, aber nicht Teil der Verantwortung der aktuellen Klasse ist, sollte es wahrscheinlich in einer separaten Klasse / Modul / was auch immer-deine-Sprache-hat sein. Da dies Teil der Verantwortung der neuen Klasse / des neuen Moduls ist, muss es veröffentlicht werden. Da Sie das Testen erwähnen, stelle ich fest, dass Sie die Methode dabei nicht unbedingt verspotten müssen. Als "Implementierungsdetail" hat das Verspotten während der Tests nur geringen Nutzen.
jpmc26

Antworten:

76

Eine reine Funktion könnte immer noch ein Implementierungsdetail sein. Obwohl die Funktion keinen Schaden anrichten kann (im Hinblick darauf, wichtige Invarianten / Verträge nicht zu brechen), verlieren sowohl der Autor als auch die Benutzer dieser Klasse / dieses Moduls / Pakets, indem sie sie offenlegen. Der Autor verliert, weil er es jetzt nicht mehr entfernen kann, auch wenn sich die Implementierung ändert und die Funktion für ihn nicht mehr nützlich ist. Die Benutzer verlieren, weil sie zusätzliche Funktionen durchsehen und ignorieren müssen, die für die Verwendung der API nicht relevant sind, um sie zu verstehen.

Doval
quelle
33
+1. Öffentliche Mitglieder Ihrer Klasse sind deren API. Es geht nicht um "Kann es mich verletzen, wenn es öffentlich ist?", Sondern um "Sollte es Teil der API sein?"
Ordous
1
Das Einzige, was ich hinzufügen möchte, ist, dass Sie, wenn Sie ähnliche Funktionen an anderen Orten sehen, diese in eine andere Klasse umgestalten, damit mehrere Klassen davon Gebrauch machen können. Oder definieren Sie es zunächst in einer separaten Klasse, wenn dies ein Problem ist.
jpmc26
1
Ich würde die öffentlichen Klassen besiegeln, die nicht dazu bestimmt sind, ebenfalls erweitert zu werden.
Den
+1, um darauf hinzuweisen, dass das Ausblenden oder Anzeigen einer Funktion (oder Klasse) in erster Linie eine Frage des Schutzes und der Pflege einer API ist und nicht mit der Art des Codes zusammenhängt.
Marco
42

Die Frage ist rückwärts.

Sie suchen nicht nach einem Grund, eine Funktion nicht öffentlich zu machen. Es ist eine falsche Einstellung, um mit (meiner Meinung nach) zu beginnen. Die Argumentation sollte in die andere Richtung gehen.

Mit anderen Worten - fragen Sie nicht "warum sollte ich es privat machen?". Fragen Sie: "Warum sollte ich es veröffentlichen?"

Im Zweifelsfall nicht aussetzen. Es ist ein bisschen wie Ockhams Rasiermesser - vermehren Sie nicht die Ansprüche, die über die Notwendigkeit hinausgehen.

BEARBEITEN: Adressierung der von @Telastyn in Kommentaren vorgebrachten Gegenargumente (um eine längere Diskussion dort zu vermeiden):

Ich habe das im Laufe der Zeit gehört und mich sogar eine ganze Weile dafür eingesetzt, aber meiner Erfahrung nach sind die Dinge zu privat.

Ja, es ist manchmal mühsam, wenn eine Klasse zur Vererbung geöffnet ist, aber Sie können einige private Methoden (deren Verhalten Sie ändern möchten) nicht überschreiben.

Aber protectedwürde ausreichen - und es ist immer noch nicht öffentlich.

Es führt zu einer Menge Code-Duplizierung und Overhead, um auf "Dinge, die nicht öffentlich sein sollten" zuzugreifen, auf die jedoch indirekt zugegriffen wird.

Wenn es problematisch wird, machen Sie es einfach öffentlich! Es gibt die Notwendigkeit, über die ich gesprochen habe :)

Mein Punkt ist, dass Sie es nicht nur für den Fall tun sollten (YAGNI und alle).

Beachten Sie, dass es immer einfacher ist, eine private Funktion öffentlich zu machen, als sie in den Datenschutz zurückzuziehen. Letzteres wird wahrscheinlich den vorhandenen Code beschädigen.

Konrad Morawski
quelle
Ich habe das im Laufe der Zeit gehört und mich sogar eine ganze Weile dafür eingesetzt, aber meiner Erfahrung nach sind die Dinge zu privat. Es führt zu einer Menge Code-Duplizierung und Overhead, um auf "Dinge, die nicht öffentlich sein sollten" zuzugreifen, auf die dennoch indirekt zugegriffen werden kann.
Telastyn
3
@Telastyn IMHO, das klingt wie die Anwendung leidet unter einigen Designfehlern; Wenn Sie dazu neigen, eine Methode bereitzustellen, weil Sie sie über diskrete Codepfade aufrufen müssen, ist dies wahrscheinlich ein Zeichen dafür, dass diese Methode in ein eigenes Modul aufgeteilt werden sollte, das eingefügt oder gegebenenfalls eingeschlossen werden kann, insbesondere, wenn es sich um eine reine Funktion handelt .
Leo
@leo - Entschuldigung, ich folge nicht. Betrachten Sie eine Funktion wie Integer kleiner als. Dies ist eine schöne reine Funktion, die speziell freigelegt wird, weil sie dann injiziert und gegebenenfalls einbezogen (oder einfach verwendet) werden kann.
Telastyn
5
@Telastyn Meiner Meinung nach ist dies ein Symptom der Philosophie "Alles ist ein Objekt / eine Klasse", kombiniert mit der Tatsache, dass einige OOP-Sprachen keine anderen zusammengesetzten Datentypen als Klassen haben. Sobald jemand zwei Werte gleichzeitig übergeben muss, schreibt er am Ende eine Klasse, und dann werden Sie von jedem Mitarbeiter und jeder Stilprüfungssoftware aufgefordert, diese Felder als privat zu kennzeichnen.
Doval
@ Telastyn Richtig. Ich meine, wenn Ihre Methode 'integerLessThan' als gekapseltes Implementierungsdetail einer öffentlichen Methode gestartet wurde, Sie jedoch feststellen, dass Sie die private Methode an anderen Stellen aufrufen müssen, ist dies wahrscheinlich ein Zeichen dafür, dass die private Methode zu einer anderen Methode gehört Modul / Paket / Klasse, damit es unabhängig von der Logik, die es aufruft, importiert werden kann. Wenn Sie die Methode einfach so veröffentlichen, wie sie ist, bedeutet dies, dass sich die Methode willkürlich in dem ersten Modul befindet, in dem Sie sie nützlich fanden.
Leo
6

Ich denke nicht, dass die Entscheidung, eine Funktion zu verbergen / einzukapseln, von ihrer Reinheit abhängen sollte. Nur weil eine Funktion rein ist, heißt das nicht, dass Externe davon wissen müssen. Interessanterweise muss die Funktion, wenn sie rein und öffentlich sein soll, nicht einmal ein Instanzmitglied der Schnittstelle sein, vielleicht ist sie besser als statische Funktion geeignet. Aber auch hier hängt alles von der Absicht des Vertrags und in diesem Fall von der logischen Gruppierung der Funktionalität ab, nicht von der Reinheit der Funktion.

Bitte
quelle
5

Der Unterricht sollte nach dem Prinzip der Einzelverantwortung ablaufen . Während eine Klasse möglicherweise andere Funktionen aufrufen muss, um ihre Ziele zu erreichen, sollte sie nur Funktionen offenlegen, die Teil ihrer Einzelverantwortung sind.

Hier ist nur ein Beispiel für einen Fall, in dem die Sichtbarkeit ein Problem verursachen könnte .

Stellen Sie sich eine Klasse vor, die Widgets frobniziert. Vielleicht benötigt es als Teil seines Frobnication-Codes eine Utility-Funktion, die einen String analysiert: Vielleicht muss es den Widget-Namen so umwandeln, dass Standard-String-Funktionen dies nicht unterstützen.

Da dies eine reine Funktion ist (String kommt rein, transformiert es irgendwie, gibt einen neuen String zurück), könnte sie ohne Konsequenz öffentlich oder privat sein. Oder könnte es?

Wenn Sie es veröffentlichen, hat Ihre Klasse jetzt zwei Aufgaben: Widgets frobnizieren und Zeichenfolgen transformieren. Dies verstößt gegen SRP und kann Probleme verursachen, wenn andere Klassen auf die Funktion angewiesen sind. Da dies Ihrer Meinung nach nur klassenintern verwendet wird, ändern Sie möglicherweise die Benutzeroberfläche oder die Sichtbarkeit. Jetzt sind Klassen in anderen Teilen des Systems kaputt.

Wenn Sie die Funktion privat halten, hat niemand die Möglichkeit, sich auf Code zu verlassen, der nicht Teil der alleinigen Verantwortung der Klasse ist.


quelle
2
Ich würde argumentieren, dass es nur Funktionen enthalten sollte , die Teil seiner einzigen Verantwortung sind, nicht nur offen zu legen.
Telastyn
1
Ich denke, es gibt eine Grauzone. Benötigen Sie wirklich eine separate StringTransformerKlasse, um zwei oder drei Codezeilen zu kapseln, die nur an einer Stelle verwendet werden? Ich stimme zu, dass es am besten ist, wenn Code an mehreren Stellen verwendet wird, ihn in eine neue Klasse mit einer Verantwortung zu unterteilen, aber es gibt einen Kompromiss.
Bestimmt. Richtlinien sind Richtlinien, keine Regeln.
Telastyn
@snowman In Non-Java erstellen Sie einfach eine Bibliothek mit Funktionen.
Pieter B
@PieterB es geht nicht um Java v. Um es wirklich OO zu machen, müssten Sie eine Fabrik, einige abstrakte Klassen usw.