Gibt es so etwas wie zu viele private Funktionen / Methoden?

63

Ich verstehe die Bedeutung von gut dokumentiertem Code. Ich verstehe aber auch die Bedeutung von selbstdokumentierendem Code. Je einfacher es ist, eine bestimmte Funktion visuell zu lesen, desto schneller können wir bei der Softwarewartung fortfahren.

Vor diesem Hintergrund möchte ich große Funktionen in andere kleinere Funktionen aufteilen. Aber ich mache das bis zu einem Punkt, an dem eine Klasse mehr als fünf von ihnen haben kann, nur um eine öffentliche Methode zu bedienen. Multiplizieren Sie nun fünf private Methoden mit fünf öffentlichen, und Sie erhalten ungefähr fünfundzwanzig versteckte Methoden, die von diesen öffentlichen Methoden wahrscheinlich nur einmal aufgerufen werden.

Sicher, es ist jetzt einfacher, diese öffentlichen Methoden zu lesen, aber ich kann nicht anders, als zu viele Funktionen zu haben, ist eine schlechte Übung.

[Bearbeiten]

Die Leute haben mich gefragt, warum ich denke, dass es schlecht ist, zu viele Funktionen zu haben.

Die einfache Antwort: Es ist ein Bauchgefühl.

Meiner Meinung nach kann ich nicht auf stundenlange Erfahrung in der Softwareentwicklung zurückgreifen. Es ist nur eine Ungewissheit, die mir eine "Schreibblockade" gab, aber für einen Programmierer.

In der Vergangenheit habe ich nur persönliche Projekte programmiert. Erst kürzlich bin ich zu teambasierten Projekten übergegangen. Jetzt möchte ich sicherstellen, dass andere meinen Code lesen und verstehen können.

Ich war mir nicht sicher, was die Lesbarkeit verbessern würde. Einerseits dachte ich daran, eine große Funktion durch verständliche Namen in andere kleinere zu trennen. Aber es gab eine andere Seite von mir, die sagte, dass es nur überflüssig ist.

Also bitte ich dies, mich selbst zu erleuchten, um den richtigen Weg zu finden.

[Bearbeiten]

Unten I der zwei Versionen , wie ich konnte mein Problem lösen. Die erste Lösung besteht darin, keine großen Codestücke voneinander zu trennen. Der zweite macht verschiedene Dinge.

Erste Version:

public static int Main()
{
    // Displays the menu.
    Console.WriteLine("Pick your option");
    Console.Writeline("[1] Input and display a polynomial");
    Console.WriteLine("[2] Add two polynomials");
    Console.WriteLine("[3] Subtract two polynomials");
    Console.WriteLine("[4] Differentiate two polynomials");
    Console.WriteLine("[0] Quit");
}

Zweite Version:

public static int Main()
{
    DisplayMenu();
}

private static void DisplayMenu()
{
    Console.WriteLine("Pick your option");
    Console.Writeline("[1] Input and display a polynomial");
    Console.WriteLine("[2] Add two polynomials");
    Console.WriteLine("[3] Subtract two polynomials");
    Console.WriteLine("[4] Differentiate two polynomials");
    Console.WriteLine("[0] Quit");
}

Letzterer ruft in den obigen Beispielen eine Funktion auf, die während der gesamten Laufzeit des Programms nur einmal verwendet wird.

Hinweis: Der obige Code ist verallgemeinert, entspricht jedoch der Art meines Problems.

Nun, hier ist meine Frage: welche? Wähle ich den ersten oder den zweiten aus?

Sal
quelle
1
"Zu viele Funktionen zu haben ist eine schlechte Übung." Warum? Bitte aktualisieren Sie Ihre Frage, um zu erklären, warum dies für Sie schlecht erscheint.
S.Lott
Ich würde empfehlen, in den Begriff "Klassenzusammenhalt" zu lesen - Bob Martin erörtert dies in "Clean Code".
19.
Fügen Sie andere Antworten hinzu, teilen Sie sie und benennen Sie sie. Denken Sie jedoch daran, dass die Mitgliedsvariablen einer Klasse mit zunehmendem Wachstum immer mehr zu globalen Variablen werden. Eine Möglichkeit, das Problem zu lindern (abgesehen von besseren Lösungen wie Refactoring ;-)), besteht darin, diese kleinen privaten Methoden in eigenständige Funktionen zu unterteilen, wenn dies möglich ist (da sie keinen Zugriff auf viele Zustände benötigen). Einige Sprachen erlauben Funktionen außerhalb der Klasse, andere haben statische Klassen. Auf diese Weise können Sie diese Funktion isoliert verstehen.
Pablo H
Wenn mir jemand sagen kann, warum "diese Antwort nicht nützlich ist", wäre ich dankbar. Man kann immer lernen. :-)
Pablo H
@PabloH Die von Ihnen angegebene Antwort bietet eine gute Beobachtung und einen guten Einblick in das OP, beantwortet jedoch nicht die gestellte Frage. Ich habe es in das Kommentarfeld verschoben.
maple_shaft

Antworten:

36

Multiplizieren Sie nun fünf private Methoden mit fünf öffentlichen, und Sie erhalten ungefähr fünfundzwanzig versteckte Methoden, die von diesen öffentlichen Methoden wahrscheinlich nur einmal aufgerufen werden.

Dies ist die sogenannte Kapselung , die auf höherer Ebene eine Kontrollabstraktion erzeugt . Das ist eine gute Sache.

Dies bedeutet , dass jemand Ihren Code zu lesen, wenn sie auf die erhalten startTheEngine()Methode in Ihrem Code können alle unteren Ebene Details ignorieren wie openIgnitionModule(), turnDistributorMotor(), sendSparksToSparkPlugs(), injectFuelIntoCylinders(), activateStarterSolenoid(), und alle anderen komplexen, kleinen, Funktionen, um ausgeführt werden müssen erleichtern die viel größere, abstraktere Funktion von startTheEngine().

Sofern sich das Problem, mit dem Sie sich in Ihrem Code befassen, nicht direkt mit einer dieser Komponenten befasst, können die Codewartenden fortfahren und die in einer Sandbox gekapselte Funktionalität ignorieren.

Dies hat auch den zusätzlichen Vorteil, dass Sie Ihren Code einfacher testen können. . Zum Beispiel kann ich einen Testfall schreiben turnAlternatorMotor(int revolutionRate)und seine Funktionalität völlig unabhängig von den anderen Systemen testen. Wenn es ein Problem mit dieser Funktion gibt und die Ausgabe nicht den Erwartungen entspricht, weiß ich, woran es liegt.

Code, der nicht in Komponenten zerlegt ist, ist viel schwieriger zu testen. Plötzlich betrachten Ihre Betreuer nur noch die Abstraktion, anstatt sich in messbare Komponenten einarbeiten zu können.

Mein Rat ist, weiterhin das zu tun, was Sie tun, da sich Ihr Code skalieren lässt, einfach zu warten ist und über Jahre hinweg verwendet und aktualisiert werden kann.

jmort253
quelle
3
Sie glauben also, dass es eine gute „Kapselung“ ist, einer ganzen Klasse Logik zur Verfügung zu stellen, die nur an einer Stelle aufgerufen wird?
Steven Jeuris
9
@Steven Jeuris: Solange diese Funktionen privat sind, sehe ich kein Problem damit; In der Tat finde ich, wie in dieser Antwort angegeben, leichter zu lesen. Es ist viel einfacher, eine öffentliche Funktion auf hoher Ebene zu verstehen, die nur eine Reihe von privaten Funktionen auf niedriger Ebene aufruft (die natürlich entsprechend benannt wurden). Sicher, all dieser Code hätte in die High-Level-Funktion selbst eingefügt werden können, aber dann würde es länger dauern, bis Sie den Code verstanden haben, da Sie viel mehr davon an ein und derselben Stelle durchsehen müssen.
Gablin
2
@gablin: Auf private Funktionen kann weiterhin von der gesamten Klasse aus zugegriffen werden. In diesem (anscheinend kontroversen) Artikel diskutiere ich, wie die 'globale Lesbarkeit' verloren geht, wenn zu viele Funktionen vorhanden sind. Tatsächlich sollte ein allgemeines Prinzip neben den Funktionen nur eines tun: Sie sollten nicht zu viele haben. Sie erhalten nur "lokale Lesbarkeit", wenn Sie nur aus Gründen der Kürze aufteilen, was auch durch einfache Kommentare und "Code-Absätze" erreicht werden kann. Ok, Code sollte sich selbst dokumentieren, aber Kommentare sind nicht veraltet!
Steven Jeuris
1
@Steven - Was ist leichter zu verstehen? myString.replaceAll(pattern, originalData);Oder füge ich die gesamte replaceAll-Methode in meinen Code ein, einschließlich des Zeichenarrays, das das zugrunde liegende Zeichenfolgenobjekt darstellt, jedes Mal, wenn ich die Funktionalität einer Zeichenfolge verwenden möchte?
jmort253
7
@Steven Jeuris: Du hast einen Punkt. Wenn eine Klasse zu viele Funktionen (öffentlich oder privat) hat, versucht die gesamte Klasse möglicherweise , zu viel zu tun. In solchen Fällen ist es möglicherweise besser, es in mehrere kleinere Klassen aufzuteilen, genauso wie Sie eine zu große Funktion in mehrere kleinere Funktionen aufteilen würden.
Gablin
22

Ja und nein. Der grundlegende Grundsatz lautet: Eine Methode sollte nur eines und nur eines tun

Es ist richtig, Ihre Methode in kleinere Methoden zu unterteilen. Andererseits sollten diese Methoden optimalerweise allgemein genug sein, um nicht nur dieser "großen" Methode zu dienen, sondern auch an anderen Orten wiederverwendbar zu sein. In einigen Fällen folgt eine Methode jedoch einer einfachen Baumstruktur, z. B. einer Initialize-Methode, die InitializePlugins, InitializeInterface usw. aufruft.

Wenn Sie eine wirklich große Methode / Klasse haben, ist dies normalerweise ein Zeichen dafür, dass sie zu viel bewirkt, und Sie müssen einige Umgestaltungen vornehmen, um den "Blob" aufzulösen. Perphaps verbergen eine gewisse Komplexität in einer anderen Klasse unter einer Abstraktion und verwenden die Abhängigkeitsinjektion

Homde
quelle
1
Es ist schön zu lesen, dass nicht jeder blindlings die 'one thing'-Regel befolgt! ; p Schöne Antwort!
Steven Jeuris
16
Ich teile eine große Methode oft in kleinere Methoden auf , obwohl sie nur der größeren Methode dienen. Der Grund für die Aufteilung ist, dass es einfacher zu handhaben und zu verstehen ist, anstatt nur einen großen Code-Blob zu haben (der selbst- und gut kommentiert sein kann oder nicht).
Gablin
1
Das ist auch in einigen Fällen in Ordnung, in denen Sie eine große Methode wirklich nicht vermeiden können. Wenn Sie beispielsweise eine Initialize-Methode haben, können Sie InitializeSystems, InitializePlugins usw. usw. aufrufen. Man sollte sich immer
vergewissern,
1
Das Entscheidende an jeder Methode (oder Funktion) ist, dass sie eine klare konzeptionelle Schnittstelle haben muss, es ist „Vertrag“. Wenn du das nicht genau sagen kannst, wird es Ärger geben und du hast den falschen Faktor. (Es kann eine gute Sache sein, den Vertrag explizit zu dokumentieren - oder ein Eiffel-Tool zu verwenden, um ihn durchzusetzen -, aber es ist nicht so wichtig, ihn überhaupt dort zu haben.)
Donal Fellows
3
@gablin: ein weiterer Vorteil zu berücksichtigen: wenn ich die Dinge brechen in kleinere Funktionen auf, glaube nicht , „sie nur eine andere Funktion erfüllen“, denken sie „nur eine andere Funktion dienen jetzt “. Es gab mehrere Gelegenheiten, in denen mir das Re-Factoring großer Methoden viel Zeit beim Schreiben anderer Methoden gespart hat, da die kleineren Funktionen allgemeiner und wiederverwendbarer waren.
GSto
17

Ich denke im Allgemeinen ist es besser, sich auf die Seite zu vieler Funktionen zu setzen, als auf die Seite zu weniger. Die beiden Ausnahmen von dieser Regel, die ich in der Praxis gesehen habe, betreffen die DRY- und YAGNI- Prinzipien. Wenn Sie viele nahezu identische Funktionen haben, sollten Sie diese kombinieren und Parameter verwenden, um Wiederholungen zu vermeiden. Sie können auch zu viele Funktionen haben, wenn Sie sie "nur für den Fall" erstellt haben und sie nicht verwendet werden. Ich sehe absolut nichts Falsches daran, eine Funktion zu haben, die nur einmal verwendet wird, wenn sie die Lesbarkeit und Wartbarkeit erhöht.

Karl Bielefeldt
quelle
10

Die großartige Antwort von jmort253 hat mich dazu inspiriert, mit einem "Ja, aber" zu antworten ...

Viele kleine private Methoden zu haben, ist keine schlechte Sache, aber achten Sie auf das nervige Gefühl, das Sie dazu bringt, hier eine Frage zu stellen :). Wenn Sie an einem Punkt angelangt sind, an dem Sie Tests schreiben, die sich nur auf private Methoden konzentrieren (entweder durch direkten Aufruf oder indem Sie ein Szenario einrichten, in dem einer auf eine bestimmte Weise aufgerufen wird, damit Sie das Ergebnis testen können), dann du solltest einen Schritt zurück machen.

Was Sie vielleicht haben, ist eine neue Klasse mit einer eigenen öffentlichen Schnittstelle, die versucht zu entkommen. An diesem Punkt sollten Sie überlegen, ob Sie eine Klasse extrahieren möchten, um den Teil Ihrer vorhandenen Klassenfunktionalität zu kapseln, der derzeit in einer privaten Methode ausgeführt wird. Jetzt kann Ihre ursprüngliche Klasse Ihre neue Klasse als Abhängigkeit verwenden, und Sie können direkt fokussiertere Tests für diese Klasse schreiben.

Pete Hodgson
quelle
Ich denke, das ist eine großartige Antwort und eine Richtung, in die der Code sicherlich gehen könnte. Best Practices besagen, dass der restriktivste Zugriffsmodifikator verwendet wird, der Ihnen die Funktionen bietet, die Sie benötigen. Wenn die Methoden nicht außerhalb der Klasse verwendet werden, ist private am sinnvollsten. Wenn es sinnvoller ist, die Methoden öffentlich zu machen oder in eine andere Klasse zu verschieben, damit sie an anderer Stelle verwendet werden können, kann dies auf jeden Fall auch getan werden. +1
jmort253
8

Fast jedes OO-Projekt, an dem ich jemals teilgenommen habe, litt stark unter zu großen Klassen und zu langen Methoden.

Wenn ich das Gefühl habe, einen Kommentar zu benötigen, der den nächsten Codeabschnitt erklärt, extrahiere ich diesen Abschnitt in eine separate Methode. Auf lange Sicht wird der Code dadurch kürzer. Diese kleinen Methoden werden häufiger wiederverwendet, als Sie zunächst erwarten würden. Sie sehen Möglichkeiten, einige davon in einer separaten Klasse zu sammeln. Bald denken Sie über Ihr Problem auf einer höheren Ebene nach, und die ganze Anstrengung geht viel schneller. Neue Funktionen lassen sich einfacher schreiben, da Sie auf den kleinen Klassen aufbauen können, die Sie mit diesen kleinen Methoden erstellt haben.

Kevin Cline
quelle
7

Eine Klasse mit 5 öffentlichen Methoden und 25 privaten Methoden scheint mir nicht so groß zu sein. Stellen Sie einfach sicher, dass Ihre Klassen klar definierte Verantwortlichkeiten haben, und sorgen Sie sich nicht zu sehr um die Anzahl der Methoden.

Diese privaten Methoden sollten sich jedoch auf einen bestimmten Aspekt der gesamten Aufgabe konzentrieren, jedoch nicht auf die Weise "doSomethingPart1", "doSomethingPart2". Sie gewinnen nichts, wenn diese privaten Methoden nur Teile einer willkürlich unterteilten langen Geschichte sind, von denen jede außerhalb des gesamten Kontexts bedeutungslos ist.

user281377
quelle
+1 für "Sie gewinnen nichts, wenn diese privaten Methoden nur Teile einer willkürlich unterteilten langen Geschichte sind, von denen jede außerhalb des gesamten Kontexts bedeutungslos ist."
Mahdi
4

Auszug aus R. Martins Clean Code (S. 176):

Selbst Konzepte, die so grundlegend sind wie die Beseitigung von Duplikaten, die Ausdruckskraft von Codes und das SRP, können zu weit gehen. In dem Bemühen, unsere Klassen und Methoden klein zu halten, werden möglicherweise zu viele kleine Klassen und Methoden erstellt. Diese Regel [Anzahl der Klassen und Methoden minimieren] legt nahe, dass wir auch die Anzahl der Funktionen und Klassen niedrig halten. Hohe Klassen- und Methodenzahlen sind manchmal das Ergebnis sinnlosen Dogmatismus.

user2418306
quelle
3

Einige Optionen:

  1. Das ist gut. Nennen Sie einfach die privaten Methoden sowie Ihre öffentlichen Methoden. Und nennen Sie Ihre öffentlichen Methoden gut.

  2. Abstrahieren Sie einige dieser Hilfsmethoden in Hilfsklassen oder Hilfsmodule. Stellen Sie sicher, dass diese Hilfsklassen / -module gut benannt sind und solide Abstraktionen für sich sind.

yfeldblum
quelle
1

Ich glaube nicht, dass es jemals ein Problem mit "zu vielen privaten Methoden" gibt, wenn es sich so anfühlt, als wäre es wahrscheinlich ein Symptom für eine schlechte Codearchitektur.

Hier ist, wie ich darüber denke ... Wenn die Architektur gut entworfen ist, ist es im Allgemeinen offensichtlich, wo der Code, der X ausführt, sein sollte. Es ist akzeptabel, wenn es ein paar Ausnahmen gibt - aber diese müssen wirklich außergewöhnlich sein, sonst wird die Architektur überarbeitet, um diese als Standard zu behandeln.

Wenn das Problem beim Öffnen Ihrer Klasse darin besteht, dass sie voller privater Methoden ist, die größere Teile in kleinere Teile Code aufteilen, ist dies möglicherweise ein Symptom für fehlende Architektur. Anstelle von Klassen und Architekturen wurde dieser Codebereich prozedural in einer Klasse implementiert.

Hier ein Gleichgewicht zu finden, hängt vom Projekt und seinen Anforderungen ab. Das Gegenargument dazu ist das Sprichwort, dass ein Junior-Programmierer die Lösung in 50 Codezeilen hochfahren kann, für die ein Architekt 50 Klassen benötigt.

Michael Shaw
quelle
0

Gibt es so etwas wie zu viele private Funktionen / Methoden?

Ja.

In Python gibt es das Konzept von "privat" (wie es von C ++, Java und C # verwendet wird) nicht wirklich.

Es gibt eine Art private Namenskonvention, aber das war's.

Privat kann zu schwer zu testendem Code führen. Es kann auch das "Open-Closed-Prinzip" brechen, indem Code einfach geschlossen wird.

Folglich haben private Funktionen für Leute, die Python verwendet haben, keinen Wert. Mach einfach alles öffentlich und mach es fertig.

S.Lott
quelle
1
Wie kann es das offene geschlossene Prinzip brechen? Der Code kann sowohl erweitert als auch geändert werden, unabhängig davon, ob die öffentlichen Methoden in kleinere private Methoden aufgeteilt wurden.
Bis zum