C # -Namensraum und Klassennamenskonvention für Bibliotheken

26

Ich erstelle Bibliotheken mit verschiedenen kleinen Hilfsprogrammfunktionen in C # und versuche, eine Namensraum- und Klassennamenskonvention festzulegen. Meine derzeitige Organisation sieht folgendermaßen aus:

Company
Company.TextUtils
    public class TextUtils {...}
Company.MathsUtils
    public class MathsUtils {...}
    public class ArbitraryPrecisionNumber {...}
    public class MathsNotation {...}
Company.SIUnits
    public enum SISuffixes {...}
    public class SIUnits {...}

Ist dies eine gute Möglichkeit, den Namespace und die Klassen zu organisieren, oder gibt es eine bessere Möglichkeit? Insbesondere scheint es so, als ob der gleiche Name im Namespace und im Klassennamen (z. B. Company.TextUtilsNamespace und TextUtilsKlasse) nur eine Verdoppelung ist, und dies deutet darauf hin, dass das Schema besser sein könnte.

Benutzer
quelle
5
Die Microsoft-Richtlinien empfehlen singuläre Namen für Enums (also SISuffixstatt SISuffixes), und ich denke, dass Singular besser liest. Suffix.A liest als "Suffix A". Außerdem vermeide ich im Allgemeinen, Namen von einer höheren Ebene in der Hierarchie aus zu wiederholen. Das heißt, stattdessen Company.SIUnits.SISuffixeswürde ich mich zu Company.SI.SuffixundCompany.SI.Unit
Harrison Paine am
2
@richzilla thecodelesscode.com/case/220
Tin Man

Antworten:

9

Es ist sehr schwierig festzustellen, wie effektiv Ihre Namensstrategie unabhängig vom Rest Ihres Codes ist.

Der Namespace sollte eine Vorstellung davon geben, wo er in die breite Struktur Ihrer Codebasis passt, und der Klassenname würde normalerweise beschreiben, welche Art von Funktionalität oder Konzept er in diesem Bereich darstellt.

Abgesehen von den Einschränkungen in Ihrem speziellen Beispiel, wenn Sie wahrscheinlich viel mehr Util-Typklassen haben, würde ich in Betracht ziehen, diese unter Company.Utils.Mathsusw. zu stellen. Auf diese Weise müssen Sie bereits Funktionen hinzufügen, die mehreren Versorgungsbereichen gemeinsam sind ein natürlicher Ort dafür.

Richzilla
quelle
1
Das hört sich nach einer guten Idee an. Hör auf, dir zu viele Sorgen zu machen, wirf einfach alles in "Company.Util".
Benutzer
30

Es gibt ein nettes Dokument, das viele Regeln enthält, die Sie befolgen sollten, um mit Microsoft übereinzustimmen : Framework Design Guidelines .

Eine Sache, die Sie ändern sollten: Benennen Sie Klassen nicht als ihren Namespace. Das wird zu Compiler-Verwechslungen führen. Tu es einfach nicht. Suchen Sie einen besseren Namen für die Klasse des Namespaces.

nvoigt
quelle
3
Der direkte Link zu den Namespace-Richtlinien: msdn.microsoft.com/en-us/library/ms229026(v=vs.110).aspx
Pagotti
1
Was zufällig oder auch nicht derselbe Name eines großartigen Buches zu diesem Thema ist, das auch von Microsoft-Mitarbeitern geschrieben wurde, die an der Erstellung des DotNet-Frameworks mitgewirkt haben, und das einige Anmerkungen dazu enthält, was sie richtig gemacht haben und wo sie etwas falsch gemacht haben. Lohnt sich zu lesen: amazon.com/Framework-Design-Guidelines-Conventions-Libraries/dp/…
Machado
@nvoigt, können Sie bitte Ihre Antwort bearbeiten, um ein Zitat aus dem Link hinzuzufügen, den unser Freund Pagotti bereitgestellt hat: " Verwenden Sie NICHT denselben Namen für einen Namespace und geben Sie in diesen Namespace einen Namen ein. Verwenden Sie beispielsweise Debug nicht als Namespace-Namen und Geben Sie dann auch eine Klasse mit dem Namen Debug im selben Namespace an. Mehrere Compiler erfordern, dass diese Typen vollständig qualifiziert sind. "
Machado
2
Vorsichtsmaßnahme: Manchmal ist ein Produktname besser als die Verwendung eines Firmennamens. In einigen Szenarien, in denen ein Unternehmen aufgekauft wurde, mussten wir neue Namespaces generell erstellen und erneut veröffentlichen. Ich denke, das ist sehr gering, wollte aber darauf hinweisen.
Jon Raynor
1
Die Framework-Design-Richtlinien von Microsoft sind höchst fragwürdig, enthalten falsche Aussagen zur Funktionsweise von .NET und werden von Microsoft selbst häufig nicht befolgt. Ihre Benennungsrichtlinien sind gut, aber ich würde es vermeiden, die allgemeinen Richtlinien mit der Aussage zu verknüpfen, dass "Sie ihnen folgen sollten".
Carl Leth
6

Es ist schon eine Weile her, dass ich mit C # oder dem .NET-Ökosystem gearbeitet habe. Daher kann ich nicht sagen, welche Best Practices derzeit empfohlen werden. Ich würde eine Änderung Ihrer Namen wie folgt empfehlen:

Company
Company.Text
    public class TextUtils {...}
Company.Math
    public class MathUtils {...}
    public class ArbitraryPrecisionNumber {...}
    public class MathsNotation {...}
Company.SI
    public enum SISuffixes {...}
    public class SIUnits {...}

Ich habe hier "Utils" aus den Namespace-Namen entfernt. Ich denke, dass "Util" nicht in einem Namespace sein sollte, weil der Company.TextNamespace eines Tages Klassen enthalten könnte, die keine Textdienstprogrammklassen sind . Der Company.MathNamespace enthält bereits,ArbitraryPrecisionNumber was anscheinend kein "Dienstprogramm" ist.

Das Entfernen von "Util" aus dem Namespace beseitigt auch Verwirrungen, die auftreten können, wenn zwei Dinge mit demselben Namen vorliegen (wie in Roberts Antwort erwähnt: https://softwareengineering.stackexchange.com/a/340440/13156 ).

Auf eine andere Weise hätten Sie den Code organisieren können:

Company
Company.Util
    public class TextUtils {...}
    public class MathUtils {...}
Company.Math
    public class ArbitraryPrecisionNumber {...}
    public class MathsNotation {...}
Company.SI
    public enum SISuffixes {...}
    public class SIUnits {...}

In diesem Fall befinden sich alle Dienstprogrammklassen im gleichen Namespace. Es gibt keinen Company.TextNamespace mehr, da es nur eine Utility-Klasse gab.

Persönlich finde ich die erste Option etwas klarer.

FrustratedWithFormsDesigner
quelle
4

Das Verwenden des gleichen Namens für den Namespace und die Klasse darin ist problematisch.

  • Wenn der Namespace mehr als eine Klasse enthält, warum werden andere Klassen dort abgelegt? es fühlt sich nicht richtig an, weil das Ziel des Namens des Namespaces darin besteht, alle Klassen darin zu beschreiben , nicht nur eine. Wenn Sie zum Beispiel haben JsonSerialization, BinarySerializationund XmlSerializationKlassen in einem Namespace, würde es Sinn macht Ihren Namensraum zu nennen XmlSerialization?

    Was geschieht in der Regel ist , dass aufgrund einer Extraktion einer Klasse aus einem vorhandenen Namespace oder eine Zusammenführung zwischen mehreren Klassen oder anderer Form der Reorganisation, können Sie sich mit einem Namespace finden eine enthält wichtige Klasse; Nach und nach werden dort kleinere Klassen abgelegt, da sie leicht mit der ursprünglichen Klasse verwandt sind. Zum Beispiel kann ein Namensraum LogParserkann eine einzelne Klasse enthält LogParser, und dann jemand setzt LogConverter, weil es ganz an den Parser verwendet ist, dann LogSearcherusw. Das Problem hierbei ist , dass der Name des Namespace nicht geändert wurde: sobald LogConverterhinzugefügt wurde, das Name hätte geändert werden sollen LogsProcessingoder einfach Logs.

  • Wenn der Namespace nur eine Klasse enthält, weist dies möglicherweise auf ein Problem in der Codeorganisation hin.

    Obwohl ich einige Male Situationen erlebt habe, in denen sich eine einzelne Klasse mit den richtigen SOLID-Prinzipien von allem anderen in der Codebasis stark unterschied und daher in einen dedizierten Namespace gestellt wurde, sind solche Fälle selten. Häufiger weist dies auf ein Problem hin. In ähnlicher Weise hindert nichts Sie daran, eine Klasse zu haben, die eine einzelne Methode enthält. Meistens weisen solche Klassen jedoch auf ein Problem hin.

    Selbst wenn Ihr Namespace nur eine Klasse enthält, gibt es normalerweise eine Möglichkeit, beim Benennen der Klasse spezifischer und beim Benennen des Namespace allgemeiner vorzugehen. Stellen Sie sich eine Anwendung vor, die unter anderem zu einem bestimmten Zeitpunkt Dateien, die im ABC-Format geschrieben wurden, in das DEF-Format konvertieren soll. Die Konvertierung erfordert keine Deserialisierung / Serialisierung von / zu den Geschäftsobjekten und erfolgt durch Anwenden einer Reihe regulärer Ausdrücke, die kurz genug sind, um in die Konvertierungsklasse selbst gestellt zu werdenAbcToDefConverter. Die gesamte Konvertierungslogik benötigt ungefähr 80 LLOC in ungefähr zehn voneinander abhängigen Methoden. Dies scheint eine Situation zu sein, in der es absolut nicht erforderlich ist, die vorhandene Klasse weder zu teilen noch zusätzliche Klassen zu erstellen. Da der verbleibende Teil der Anwendung nichts mit Konvertierungen zu tun hat, kann die Klasse nicht mit anderen Klassen in vorhandenen Namespaces gruppiert werden. So erstellt man einen Namespace namens AbcToDefConverter. Daran ist zwar an sich nichts auszusetzen, man könnte aber auch einen allgemeineren Namen verwenden, wie z Converters. In Sprachen wie Python, in denen kürzere Namen bevorzugt werden und Wiederholungen angewendet werden, kann dies sogar der Fall sein converters.Abc_To_Def.

Daher tun für Namensräume unterschiedliche Namen verwenden als für die Klassen , die sie enthalten. Der Name einer Klasse sollte angeben, was die Klasse tut, während der Name des Namespaces die Gemeinsamkeiten aller darin enthaltenen Klassen hervorheben sollte.


Utility-Klassen sind übrigens von Natur aus falsch: Anstatt etwas Bestimmtes wie Arithmetik mit willkürlicher Genauigkeit zu enthalten, enthalten sie eher alles, was in anderen Klassen nicht seinen Weg gefunden hat . Dies ist einfach eine schlechte Benennung, genau wie ein MiscellaneousVerzeichnis auf einem Desktop - etwas, das auf die mangelnde Organisation hinweist.

Die Benennung erfolgt aus einem Grund: um Ihnen das Leben zu erleichtern, wenn Sie später etwas suchen müssen. Wenn Sie ein Diagramm zeichnen müssen, versuchen Sie möglicherweise, nach "Diagramm" oder "Diagramm" zu suchen. Wenn Sie die Art und Weise ändern möchten, in der eine App Rechnungen erstellt, suchen Sie nach "Rechnung [e / ing]" oder "Rechnung". Stellen Sie sich einen Fall vor, in dem Sie sich sagen: "Hm, diese Funktion sollte wahrscheinlich in verschiedenen Anwendungen zu finden sein." Ich kann nicht

Sehen Sie sich .NET Framework an. Sind die meisten dieser Klassen keine Utility-Klassen? Ich meine, sie haben ein paar Dinge mit der Geschäftsdomäne zu tun. Wenn ich an einer Finanz-App, einer E-Commerce-Website oder einer Bildungsplattform der nächsten Generation arbeite, ist das Serialisieren von XML, das Lesen von Dateien, das Ausführen von SQL-Abfragen oder das Durchführen von Transaktionen alles Nützliche. Sie werden jedoch nicht UtilitySerializationoder aufgerufen UtilityTransactionund befinden sich nicht im UtilityNamespace. Sie haben Eigennamen, die es ermöglichen (und dank .NET-Entwicklern ganz einfach!), Sie zu finden, wenn ich sie brauche.

Dasselbe gilt für Ihre Klassen, die Sie normalerweise in Ihren Apps wiederverwenden. Sie sind keine Gebrauchsklassen. Es sind Klassen, die bestimmte Dinge tun, und die Dinge, die sie tun, sollten eigentlich die Namen der Klassen sein.

Stellen Sie sich vor, Sie haben Code erstellt, der sich mit Einheiten und der Umrechnung von Einheiten befasst. Sie können es benennen Utilityund von Ihren Kollegen gehasst werden; oder du kannst es benennen Unitsund UnitsConversion.

Arseni Mourzenko
quelle
7
"Gebrauchsklassen sind von Natur aus falsch". Interessant. Welche Lösung schlagen Sie beispielsweise für eine Math-Funktionsdienstprogrammklasse vor? Sollte eine Instanz eines Math-Objekts wirklich erforderlich sein, um Funktionen zu verwenden, die über die grundlegenden arithmetischen Operatoren hinausgehen?
FrustratedWithFormsDesigner
1
Ich stimme Ihnen zu, aber was schlagen Sie als Alternative vor?
Benutzer
4
Ich habe die Erstellung einer "utils" -Bibliothek für die längste Zeit verzögert, aber irgendwann muss man einfach nachgeben. Die Alternative besteht darin, DLLs mit etwa 3 Codezeilen zu haben, was nur umständlich ist (und mich an das Durcheinander von Abhängigkeiten erinnert, die node.js ist) ) oder kopieren Sie Ihre Dienstprogrammmethoden und fügen Sie sie in jede einzelne Klasse ein, die sie möglicherweise benötigt (um diese "Sammlung zufälliger Methoden" -Klasse zu vermeiden). IMO am Ende ist es ein notwendiges Übel.
Matti Virkkunen
3
@FrustratedWithFormsDesigner: einfach ... Math? Ich verstehe nicht, wie das Hinzufügen von UtilitySuffixen zu allem unser Leben erleichtern würde. Würden Sie bei Verwendung der .NET- System.Math.Min()Methode lieber schreiben SystemUtility.MathUtility.Min()? In allen Fällen habe ich meine Antwort stark bearbeitet, daher sollte es jetzt klarer sein.
Arseni Mourzenko