Best Practices für die Verwendung von Namespaces in C ++ [closed]

38

Ich habe vor ein paar Monaten Onkel Bobs Clean Code gelesen und er hatte tiefgreifende Auswirkungen auf die Art und Weise, wie ich Code schreibe. Selbst wenn es so aussah, als würde er Dinge wiederholen, die jeder Programmierer wissen sollte, führt das Zusammenfügen und Umsetzen dieser Dinge zu einem viel saubereren Code. Insbesondere empfand ich es als unglaublich nützlich, große Funktionen in viele kleine Funktionen und große Klassen in viele kleine Klassen aufzuteilen.

Nun zur Frage. Die Beispiele des Buches sind alle in Java, während ich in den letzten Jahren in C ++ gearbeitet habe. Wie würden sich die Ideen in Clean Code auf die Verwendung von Namespaces auswirken, die in Java nicht vorhanden sind? (Ja, ich kenne die Java-Pakete, aber es ist nicht wirklich dasselbe.)

Ist es sinnvoll, die Idee, viele kleine Einheiten mit jeweils klar definierter Verantwortung zu erstellen, auf Namespaces anzuwenden? Soll eine kleine Gruppe zusammengehöriger Klassen immer in einen Namespace eingeschlossen werden? Ist dies der Weg, um die Komplexität von vielen kleinen Klassen zu verwalten, oder wären die Kosten für die Verwaltung vieler Namespaces unerschwinglich?

Bearbeiten: Meine Frage wird in diesem Wikipedia-Eintrag über Paketprinzipien beantwortet .

Dima
quelle
7
Ein weiteres Beispiel für eine gute und realistische Entwicklerfrage wurde geschlossen . Wenn dies nicht die StackExchange-Site ist, auf der Sie nach Best Practices für Entwickler gefragt werden, welche?
Sam Goldberg
@SamGoldberg: es wird besser. Ich habe hier eine gute Antwort auf diese Frage gefunden: en.wikipedia.org/wiki/Package_Principles . Ich habe diese Antwort gepostet, und ein Moderator hat sie gelöscht, weil ich einfach den Link gepostet habe und keine Zeit hatte, den Inhalt zu kopieren / einzufügen.
Dima
1
Ich denke, meine einzige Kritik an der Frage wäre, anstatt mehrere Fragen am Ende zu stellen, zu versuchen, sie in einer einzigen Frage zusammenzufassen, z. ".
Sam Goldberg

Antworten:

22

(Ich habe Clean Code nicht gelesen und kenne nicht viel Java.)

Ist es sinnvoll, die Idee, viele kleine Einheiten mit jeweils klar definierter Verantwortung zu erstellen, auf Namespaces anzuwenden?

Ja, genau wie beim Refactoring in mehrere Klassen und Funktionen.

Soll eine kleine Gruppe zusammengehöriger Klassen immer in einen Namespace eingeschlossen werden?

Ohne tatsächlich zu antworten: Ja, Sie sollten mindestens einen Namespace der obersten Ebene verwenden. Dies kann auf Projekt, Organisation oder was auch immer basieren, aber die Verwendung weniger globaler Namen verringert Namenskonflikte. Ein einziger Namespace, in dem alle anderen Elemente gruppiert werden, führt nur einen globalen Namen ein. (Ausgenommen externe "C" -Funktionen, dies liegt jedoch an der C-Interoperabilität und wirkt sich nur auf andere externe "C" -Funktionen aus.)

Sollte eine kleine Gruppe verwandter Klassen in einen ihnen gewidmeten Namespace eingeschlossen werden? Wahrscheinlich. Besonders wenn Sie feststellen, dass Sie ein gemeinsames Präfix für diese Klassen verwenden - FrobberThing, FrobberThang, FrobberDoohickey - sollten Sie einen Namespace in Betracht ziehen - frobber :: Thing und so weiter. Dies würde sich immer noch unter Ihrem Stamm-Namespace oder einem anderen Namespace befinden, wenn sie Teil eines größeren Projekts sind.

Ist dies der Weg, um die Komplexität von vielen kleinen Klassen zu verwalten, oder wären die Kosten für die Verwaltung vieler Namespaces unerschwinglich?

Anhand des obigen Beispiels für Präfixnamen ist es nicht schwieriger, frobber :: Thing als FrobberThing zu verwalten. Mit einigen Tools wie Dokumentation und Code-Vervollständigung kann es sogar einfacher sein. Es gibt einen Unterschied zu ADL, aber dies kann zu Ihren Gunsten funktionieren: Weniger Namen in zugeordneten Namespaces vereinfachen das Herausfinden von ADL, und Sie können mithilfe von Deklarationen bestimmte Namen in den einen oder anderen Namespace einfügen.

Mit Namespace-Aliasnamen können Sie einen kürzeren Namen für einen längeren Namespace in einem bestimmten Kontext verwenden, was wiederum eine einfachere Verwendung ermöglicht:

void f() {
  namespace CWVLN = Company_with_very_long_name;  // Example from the standard.
  // In this scope, use CWVLN::name instead of Company_with_very_long_name::name.
  namespace fs = boost::filesystem;  // Commonly used.
}

Betrachten Sie Boost, das einen einzigen Root-Namespace, boost und dann viele Subnamespaces - boost :: asio, boost :: io, boost :: filesystem, boost :: tuples usw. - für verschiedene Bibliotheken hat. Einige Namen werden in den Root-Namespace "befördert" :

Alle Definitionen befinden sich im Namespace :: boost :: tuples, die gebräuchlichsten Namen werden jedoch mithilfe von Deklarationen in den Namespace :: boost angehoben. Diese Namen sind: tuple, make_tuple, tie and get. Außerdem werden ref und cref direkt unter dem Namespace :: boost definiert.

Der größte Unterschied zu Sprachen mit "echten" Modulen besteht darin, wie häufig eine flachere Struktur verwendet wird. Dies geschieht meistens, weil dies so funktioniert, es sei denn, Sie nehmen zusätzliche, spezifische Anstrengungen in Kauf, um verschachtelte Namen zu definieren.

Fred Nurk
quelle
+1 @Fred Nurk für die vollständige Analyse anstelle von Antworten in einer Zeile. Sie müssen das Buch nicht lesen, um zu wissen, worum es bei der Frage geht. Dieser Kommentarthread enthüllt die Konzepte und Unterschiede, die ein C ++ - und ein Java-Programmierer in diesem Buch haben könnten. Clean Code Kommentar in Amazon
Marlon
8

Sie sollten einen Master-Namespace für Ihren gesamten Code haben. Dies unterscheidet es von externem Code in Bezug auf Namespaces.

In Ihrem Master-Namespace können Sie abhängig von der Größe und Komplexität Unter-Namespaces öffnen. Hier bedeuten Namen eindeutig etwas in einem Kontext, und dieselben Namen können in einem anderen Kontext verwendet werden.

Insbesondere wenn Sie einen allgemein klingenden Namen wie FileInfo haben, der etwas Bestimmtes in einem Kontext bedeutet, setzen Sie ihn in einen Namespace.

Sie können dazu auch eine Klasse verwenden, obwohl eine Klasse nicht erweiterbar ist, sodass Sie der Klasse keine neuen Deklarationen hinzufügen können, ohne ihren Header zu ändern.

Goldesel
quelle
3

Namespaces sind kein Modulkonzept, daher würde ich sie nur dort verwenden, wo Namenskonflikte auftreten können.

Brainlag
quelle
4
Nicht sicher was du meinst. Warum ist eine Gruppe zusammengehöriger Klassen, die in einen Namespace eingeschlossen sind, kein Modul?
Dima
Es gibt keine Einschränkungen im Namespace. In einem Modul kann man sagen, dass auf eine bestimmte Klasse, Funktion oder Variable nur innerhalb des Moduls zugegriffen werden kann. In einem Namespace ist dies nicht möglich, sondern lediglich ein Präfix vor dem Namen.
Brainlag
3
Sieht so aus, als wären wir uns in der Definition eines Moduls nicht einig . Für mich ist alles, was zusammengehörige Entitäten gruppiert und einen beschreibenden Namen hat, ein Modul, unabhängig davon, ob es verwelkt oder nicht, es bietet eine Kapselung.
Dima
3
Die Zugriffsbeschränkung ist orthogonal zu Modulen. Tatsächlich haben Sie erfolgreiche Systeme wie Python's, die definitiv "modulartiger" sind als die Namespaces von C ++, aber keinerlei Zugriffsbeschränkungen auferlegen.
Fred Nurk
Ich glaube, dies ist eine der besten Methoden, die in Scott Meyers Büchern aufgeführt sind
Raphael,
1

Java hat Namespaces, die werden einfach nicht so genannt. In javax.swing.* javaxist ein Namespace und swingist ein Subnamespace. Ich habe das Buch nicht gelesen, um zu wissen, was es über Java-Pakete aussagt, aber die gleichen Prinzipien gelten so ziemlich direkt für Namespaces in jeder Sprache.

Eine gute Heuristik ist, dass Sie einen Namespace verwenden, wenn Sie feststellen, dass Sie immer wieder dasselbe Präfix für Klassen eingeben möchten. Ich habe zum Beispiel kürzlich einige Klassen mit den Namen OmciAttribute, OmciAlarm, OmciMe usw. geschrieben und festgestellt, dass ich Omci in einen eigenen Namespace unterteilen muss.

Karl Bielefeldt
quelle
1

Ich mag tiefe Namespaces (was normalerweise drei Ebenen bedeutet).

  • Ich habe den Firmennamen.
  • app / util / lib / etc
  • Projektname / oder Paket entsprechend

Abhängig von der Situation kann ich eine weitere Stufe haben

  • Details (plattformspezifische Implementierungsdetails)
  • utils (Dienstprogrammobjekt, das noch nicht in das allgemeine Dienstprogramm verschoben wurde).
  • was auch immer ich brauche.
Martin York
quelle