Najlepsze praktyki korzystania z przestrzeni nazw w C ++ [zamknięte]

38

Kilka miesięcy temu przeczytałem Czysty kod wuja Boba , który miał ogromny wpływ na sposób, w jaki piszę kod. Nawet jeśli wydawało się, że powtarza rzeczy, które każdy programista powinien wiedzieć, zebranie ich wszystkich razem i wdrożenie ich w praktyce skutkuje znacznie czystszym kodem. W szczególności odkryłem, że dzielenie dużych funkcji na wiele drobnych funkcji i dzielenie dużych klas na wiele małych klas jest niezwykle przydatne.

Teraz pytanie. Wszystkie przykłady tej książki są w Javie, podczas gdy ja pracuję w C ++ od kilku lat. W jaki sposób pomysły w Czystym Kodzie obejmowałyby stosowanie przestrzeni nazw, które nie istnieją w Javie? (Tak, wiem o pakietach Java, ale tak naprawdę to nie to samo.)

Czy ma sens zastosowanie idei tworzenia wielu małych bytów, z których każdy ma jasno określoną odpowiedzialność, do przestrzeni nazw? Czy mała grupa pokrewnych klas powinna być zawsze zawinięta w przestrzeń nazw? Czy jest to sposób na zarządzanie złożonością posiadania wielu małych klas, czy też koszt zarządzania wieloma przestrzeniami nazw byłby zbyt wysoki?

Edycja: Odpowiedzi na moje pytanie w tym wpisie w Wikipedii na temat zasad pakietów .

Dima
źródło
7
Kolejny przykład dobrego i prawdziwego pytania programisty zamkniętego . Jeśli nie jest to witryna stosu wymiany z pytaniami o najlepsze praktyki programistów, co to jest?
Sam Goldberg,
@SamGoldberg: robi się lepiej. Znalazłem dobrą odpowiedź na to pytanie tutaj: en.wikipedia.org/wiki/Package_Principles . Opublikowałem tę odpowiedź, a moderator ją usunął, ponieważ po prostu opublikowałem link i nie miałem czasu na kopiowanie / wklejanie treści.
Dima,
1
Myślę, że moją jedyną krytyką tego pytania byłoby, zamiast zadawania wielu pytań na końcu, spróbować podsumować je w jednym pytaniu, np. „Jakie powinny być kryteria określające, czy elementy powinny znajdować się w tej samej przestrzeni nazw, czy w różnych przestrzeniach nazw? „.
Sam Goldberg,

Odpowiedzi:

22

(Nie przeczytałem Czystego Kodu i nie znam dużo Javy).

Czy ma sens zastosowanie idei tworzenia wielu małych bytów, z których każdy ma jasno określoną odpowiedzialność, do przestrzeni nazw?

Tak, podobnie jak w przypadku refaktoryzacji na wiele klas i wiele funkcji.

Czy mała grupa pokrewnych klas powinna być zawsze zawinięta w przestrzeń nazw?

Bez odpowiedzi: tak, powinieneś użyć przynajmniej jednej przestrzeni nazw najwyższego poziomu. Może to być oparte na projekcie, organizacji lub czymkolwiek innym, ale użycie kilku nazw globalnych ograniczy konflikty nazw. Pojedyncza przestrzeń nazw do grupowania wszystkich pozostałych elementów wprowadza tylko jedną nazwę globalną. (Z wyjątkiem zewnętrznych funkcji „C”, ale wynika to z interoperacyjności C i wpływa tylko na inne zewnętrzne funkcje „C”.)

Czy mała grupa powiązanych klas powinna być zapakowana w dedykowaną im przestrzeń nazw ? Prawdopodobnie. Zwłaszcza jeśli używasz wspólnego przedrostka w tych klasach - FrobberThing, FrobberThang, FrobberDoohickey - powinieneś rozważyć przestrzeń nazw - frobber :: Thing i tak dalej. To nadal znajdowałoby się w głównej przestrzeni nazw lub w innej przestrzeni nazw, jeśli są one częścią większego projektu.

Czy jest to sposób na zarządzanie złożonością posiadania wielu małych klas, czy też koszt zarządzania wieloma przestrzeniami nazw byłby zbyt wysoki?

Biorąc powyższy przykład prefiksowanych nazw, zarządzanie frobber :: Thing nie jest trudniejsze niż FrobberThing. Może być nawet łatwiej z niektórymi narzędziami, takimi jak dokumentacja i uzupełnianie kodu. Istnieje różnica w ADL, ale może to działać na twoją korzyść: mniej nazw w powiązanych przestrzeniach nazw ułatwia ADL, aby łatwiej je rozgryźć, i możesz użyć deklaracji, aby wstrzyknąć określone nazwy do jednej lub innej przestrzeni nazw.

Aliasy przestrzeni nazw pozwalają na użycie krótszej nazwy dla dłuższej przestrzeni nazw w określonym kontekście, co ponownie pozwala na łatwiejsze użycie:

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.
}

Rozważ Boost, który ma jedną główną przestrzeń nazw, boost, a następnie wiele podprzestrzeni - boost :: asio, boost :: io, boost :: system plików, boost :: krotki itp. - dla różnych bibliotek. Niektóre nazwy są „promowane” do głównej przestrzeni nazw:

Wszystkie definicje znajdują się w przestrzeni nazw :: boost :: krotki, ale najpopularniejsze nazwy są podnoszone do przestrzeni nazw :: boost przy użyciu deklaracji. Te nazwy to: krotka, make_tuple, tie i get. Ponadto ref i cref są definiowane bezpośrednio w przestrzeni nazw :: boost.

Największą różnicą w stosunku do języków z „prawdziwymi” modułami jest to, jak często stosuje się bardziej płaską strukturę, co dzieje się głównie dlatego, że tak to działa, chyba że podejmiesz dodatkowy, konkretny wysiłek, aby zdefiniować zagnieżdżone nazwy.

Fred Nurk
źródło
+1 @ Fred Nurk za pełną analizę zamiast odpowiedzi w jednej linii. Nie musisz czytać książki, aby wiedzieć, o co chodzi w pytaniu. Ten wątek komentarza przedstawia koncepcje i różnice, jakie mogą mieć C ++ i programista Java w tej książce. Wyczyść komentarz do kodu w Amazon
Marlon
8

Powinieneś mieć jedną główną przestrzeń nazw dla całego swojego kodu. To odróżnia go od kodu zewnętrznego w odniesieniu do przestrzeni nazw.

W obrębie głównej przestrzeni nazw, w zależności od wielkości i złożoności, można otwierać podprzestrzenie nazw. To tutaj nazwy wyraźnie oznaczają coś w kontekście, a te same nazwy mogą być użyte w innym kontekście.

W szczególności, jeśli masz ogólną nazwę brzmiącą jak FileInfo, która oznacza coś konkretnego w kontekście, umieść ją w przestrzeni nazw.

Możesz także użyć do tego klasy, chociaż nie można jej rozszerzyć, więc nie możesz dodawać do niej nowych deklaracji bez modyfikacji jej nagłówka.

Dojną krową
źródło
3

Przestrzenie nazw nie są koncepcją modułu, więc używałbym ich tylko tam, gdzie mogłyby wystąpić konflikty nazw.

Brainlag
źródło
4
Nie wiem co masz na myśli. Dlaczego zestaw powiązanych klas zawinięty w przestrzeni nazw nie byłby modułem?
Dima,
Nie ma żadnych ograniczeń w przestrzeni nazw. W module można powiedzieć, że do danej klasy, funkcji lub zmiennej można uzyskać dostęp tylko z wnętrza modułu. W przestrzeni nazw nie jest to możliwe, jest to tylko przedrostek nazwy.
Brainlag
3
Wygląda na to, że nie zgadzamy się co do definicji modułu . Dla mnie wszystko, co grupuje powiązane ze sobą jednostki i ma opisową nazwę, jest modułem, niezależnie od tego, czy więdnie, czy nie, zapewnia enkapsulację.
Dima,
3
Ograniczenie dostępu jest prostopadłe do modułów. W rzeczywistości masz udane systemy, takie jak Python, które zdecydowanie są bardziej „modułowe” niż przestrzenie nazw C ++, ale nie nakładają żadnych ograniczeń dostępu.
Fred Nurk
Uważam, że jest to jedna z najlepszych praktyk wymienionych w książkach Scotta Meyera
Raphael
1

Java ma przestrzenie nazw, tak naprawdę tak się nie nazywają. In javax.swing.* javaxjest przestrzenią nazw i swingjest podprzestrzenią nazw. Nie czytałem książki, aby wiedzieć, co mówi o pakietach Java, ale te same zasady miałyby zastosowanie prawie bezpośrednio do przestrzeni nazw w dowolnym języku.

Dobra heurystyka polega na tym, że używasz przestrzeni nazw, gdy chcesz ciągle powtarzać ten sam prefiks dla klas. Na przykład ostatnio napisałem kilka klas o nazwach OmciAttribute, OmciAlarm, OmciMe itp. I zdałem sobie sprawę, że muszę rozbić Omci na własną przestrzeń nazw.

Karl Bielefeldt
źródło
1

Lubię głębokie przestrzenie nazw (co zwykle oznacza trzy poziomy).

  • Mam nazwę firmy.
  • app / util / lib / etc
  • Nazwa projektu / lub pakiet, w zależności od przypadku

W zależności od sytuacji mogę mieć jeszcze jeden poziom

  • szczegóły (szczegóły implementacji specyficzne dla platformy)
  • utils (obiekt narzędzia, który nie został jeszcze przeniesiony do ogólnego narzędzia).
  • cokolwiek potrzebuję.
Martin York
źródło