Przestrzeń nazw C # i konwencja nazewnictwa klas dla bibliotek

26

Buduję biblioteki z różnymi małymi funkcjami narzędziowymi w języku C # i próbuję zdecydować o przestrzeni nazw i konwencji nazewnictwa klas. Moja obecna organizacja wygląda następująco:

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

Czy to dobry sposób na uporządkowanie przestrzeni nazw i klas, czy jest lepszy sposób? W szczególności wydaje się, że posiadanie tej samej nazwy w przestrzeni nazw i nazwie klasy (np. Company.TextUtilsPrzestrzeni nazw i TextUtilsklasy) jest po prostu duplikacją i sugeruje, że schemat mógłby być lepszy.

użytkownik
źródło
5
Wytyczne Microsoft zalecają nazwy pojedyncze dla wyliczeń (więc SISuffixzamiast SISuffixes) i myślę, że liczba pojedyncza brzmi lepiej. Suffix.A brzmi „sufiks A”. Ponadto generalnie unikam powtarzania nazw z poziomu w hierarchii. To znaczy zamiast Company.SIUnits.SISuffixespochylać się Company.SI.SuffixiCompany.SI.Unit
Harrison Paine
2
@richzilla thecodelesscode.com/case/220
Tin Man

Odpowiedzi:

9

Bardzo trudno jest ustalić, jak skuteczna jest twoja strategia nazewnictwa w oderwaniu od reszty kodu.

Przestrzeń nazw powinna dać pojęcie, gdzie mieści się w szerokiej strukturze bazy kodu, a nazwa klasy zwykle opisuje, jaką funkcjonalność lub pojęcie reprezentuje w tym obszarze.

Odkładając na bok zastrzeżenia, w twoim konkretnym przykładzie, jeśli prawdopodobnie masz o wiele więcej klas typu „Util”, rozważę umieszczenie ich pod Company.Utils.Mathsitd. W ten sposób, jeśli chcesz dodać funkcjonalność wspólną dla wielu obszarów narzędziowych, masz już naturalna lokalizacja dla tego.

Richzilla
źródło
1
To brzmi jak dobry pomysł. Nie przejmuj się zbytnio, po prostu wrzuć to wszystko do „Company.Util”.
użytkownik
30

Jest fajny dokument, który zawiera wiele reguł, których należy przestrzegać, aby zachować zgodność z Microsoft: Framework Design Guidelines .

Jedną rzecz, którą powinieneś zmienić: nie nazywaj klas jako ich przestrzeni nazw. Doprowadzi to do pomyłek kompilatora. Po prostu nie. Znajdź lepszą nazwę dla klasy przestrzeni nazw.

nvoigt
źródło
3
Bezpośredni link do wytycznych dotyczących przestrzeni nazw: msdn.microsoft.com/en-us/library/ms229026(v=vs.110).aspx
Pagotti
1
Który przez przypadek, czy nie, to także ta sama nazwa świetnej książki na ten temat, napisanej również przez ludzi Microsoftu, którzy brali udział w tworzeniu frameworku DotNet, z kilkoma uwagami o tym, co zrobili dobrze i gdzie popełnili błąd. Warto przeczytać: amazon.com/Framework-Design-Guidelines-Conventions-Libraries/dp/…
Machado
@nvoigt, czy mógłbyś edytować swoją odpowiedź, aby dodać cytat z linku, który podał nasz przyjaciel Pagotti: „ NIE używaj tej samej nazwy dla przestrzeni nazw i nie pisz w tej przestrzeni nazw. Na przykład nie używaj Debugowania jako nazwy przestrzeni nazw i następnie zapewnij klasę o nazwie Debug w tej samej przestrzeni nazw. Kilka kompilatorów wymaga, aby takie typy były w pełni kwalifikowane. ”
Machado
2
Uwaga: Czasami nazwa produktu jest lepsza niż używanie nazwy firmy. Byłem w kilku scenariuszach, w których firma została wykupiona, a my musieliśmy stworzyć nowe przestrzenie nazw dla wszystkich i ponownie wydać. Myślę, że to bardzo niewielkie, ale chciałem to podkreślić.
Jon Raynor,
1
Wytyczne Microsoft dotyczące projektowania ram są wysoce wątpliwe, zawierają fałszywe stwierdzenia o tym, jak działa .NET i bardzo często nie są przestrzegane przez samego Microsoft. Ich wytyczne dotyczące nazewnictwa są dobre, ale unikałbym łączenia ogólnych wytycznych ze stwierdzeniem, że „należy ich przestrzegać”.
Carl Leth,
6

Minęło trochę czasu, odkąd pracowałem z C # lub ekosystemem .NET, więc nie mogę powiedzieć, jakie są obecnie zalecane najlepsze praktyki. Polecam zmianę twoich nazwisk w ten sposób:

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

To, co zrobiłem tutaj, to usunięcie „Utils” z nazw przestrzeni nazw. Myślę, że „Util” nie powinno znajdować się w przestrzeni nazw, ponieważ Company.Textprzestrzeń nazw może kiedyś zawierać klasy, które nie są klasami tekstowymi. Przestrzeń Company.Mathnazw już zawiera, ArbitraryPrecisionNumberco nie wydaje się „narzędziem”.

Usunięcie „Util” z przestrzeni nazw usuwa również wszelkie nieporozumienia, które mogą wyniknąć z posiadania dwóch rzeczy o tej samej nazwie (jak wspomniano w odpowiedzi Roberta: /software//a/340440/13156 )

Innym sposobem na zorganizowanie kodu:

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

W takim przypadku wszystkie klasy narzędzi znajdują się w tej samej przestrzeni nazw. Nie ma więcej Company.Textnazw, ponieważ jedyną rzeczą, nie było klasy narzędzie.

Osobiście uważam, że pierwsza opcja jest nieco bardziej przejrzysta.

FrustratedWithFormsDesigner
źródło
4

Użycie tej samej nazwy dla przestrzeni nazw i klasy w niej jest problematyczne.

  • Jeśli przestrzeń nazw zawiera więcej niż jedną klasę, dlaczego umieszczane są tam inne klasy? to nie wydaje się właściwe, ponieważ celem nazwy przestrzeni nazw jest opisanie wszystkich klas w niej zawartych , a nie tylko jednej. Na przykład, jeśli masz JsonSerialization,BinarySerialization i XmlSerializationklas w przestrzeni nazw, warto byłoby, aby wymienić swoje nazw XmlSerialization?

    Zwykle dzieje się tak, że z powodu wyodrębnienia klasy z istniejącej przestrzeni nazw lub połączenia wielu klas lub innej reorganizacji, znajdujesz się w przestrzeni nazw zawierającej główną klasę; stopniowo wprowadzane są tam mniejsze klasy, ponieważ są one nieco związane z klasą pierwotną. Na przykład przestrzeń nazw LogParsermoże zawierać jedną klasę LogParser, a następnie ktoś ją umieszcza LogConverter, ponieważ jest to dość związane z analizatorem składni LogSearcher, itd. Problem polega na tym, że nazwa przestrzeni nazw nie została zmieniona: jak tylko LogConverterzostała dodana, właściwość nazwa powinna zostać zmieniona na LogsProcessinglub po prostu Logs.

  • Jeśli przestrzeń nazw zawiera tylko jedną klasę, może to oznaczać problem w organizacji kodu.

    Chociaż kilkakrotnie widziałem sytuacje, w których jedna klasa z właściwymi zasadami SOLID bardzo różniła się od wszystkiego innego w bazie kodu i dlatego została umieszczona w dedykowanej przestrzeni nazw, takie przypadki są rzadkie. Częściej oznacza to problem. Podobnie nic nie stoi na przeszkodzie, aby klasa zawierała jedną metodę, ale najczęściej klasy takie wskazywałyby na problem.

    Nawet jeśli twoja przestrzeń nazw zawiera tylko jedną klasę, zwykle istnieje sposób, aby być bardziej szczegółowym przy nazywaniu klasy i bardziej ogólnym przy nazywaniu przestrzeni nazw. Wyobraź sobie aplikację, która między innymi powinna w danym momencie konwertować pliki zapisane w formacie ABC do formatu DEF. Konwersja nie wymaga żadnej deserializacji / serializacji do / z obiektów biznesowych i odbywa się poprzez zastosowanie zestawu wyrażeń regularnych, które są wystarczająco krótkie, aby można je było umieścić w samej klasie konwersji o nazwieAbcToDefConverter. Cała logika konwersji zajmuje około 80 LLOC w około dziesięciu współzależnych metodach - wydaje się być sytuacją, w której absolutnie nie ma potrzeby dzielenia istniejącej klasy ani tworzenia dodatkowych klas. Ponieważ pozostała część aplikacji nie ma nic wspólnego z konwersjami, klasy nie można grupować z innymi klasami w istniejących przestrzeniach nazw. Więc tworzy się przestrzeń nazwaną AbcToDefConverter. Chociaż nie ma w tym nic złego, można również użyć bardziej ogólnej nazwy, takiej jak Converters. W językach takich jak Python, w których preferowane są krótsze nazwy i narzucane jest powtarzanie, może się nawet zdarzyć converters.Abc_To_Def.

Dlatego używaj innych nazw dla przestrzeni nazw niż dla klas, które zawierają. Nazwa klasy powinna wskazywać, co robi klasa, a nazwa przestrzeni nazw powinna podkreślać to, co wspólne dla wszystkich klas w niej zawartych.


Nawiasem mówiąc, klasy użytkowe są z natury złe: zamiast zawierać coś konkretnego, na przykład arytmetykę dowolnej precyzji, raczej zawierają wszystko, co nie znalazło się w innych klasach . To po prostu złe nazewnictwo, podobnie jak Miscellaneouskatalog na pulpicie - coś, co wskazuje na brak organizacji.

Nazywanie istnieje z jakiegoś powodu: aby ułatwić Ci życie, gdy będziesz musiał później znaleźć rzeczy. Wiesz, że jeśli musisz narysować wykres, możesz spróbować wyszukać „wykres” lub „wykres”. Gdy musisz zmienić sposób generowania faktur przez aplikację, wyszukaj „faktura [e / ing]” lub „rachunek”. Podobnie, spróbuj wyobrazić sobie przypadek, w którym powiesz sobie: „hm, ta funkcja prawdopodobnie powinna być znaleziona w misc”. Nie mogę

Spójrz na .NET Framework. Czy większość z tych klas nie jest klasami użytkowymi? Mam na myśli, że mają niewiele wspólnego z domeną biznesu. Jeśli pracuję nad aplikacją finansową, witryną e-commerce lub platformą edukacyjną nowej generacji, serializowanie XML, czytanie plików lub wykonywanie zapytań SQL lub wykonywanie transakcji to wszystko, co jest użytecznością. Jednak nie są one wywoływane UtilitySerializationlub UtilityTransactionnie są w Utilityprzestrzeni nazw. Mają odpowiednie nazwy, które umożliwiają (i ułatwiają, dzięki deweloperom .NET!) Znajdowanie ich, kiedy ich potrzebuję.

To samo przychodzi do swoich klas ty powszechnie ponownego użycia w swoich aplikacjach. Nie są to klasy użytkowe. Są to klasy, które robią pewne rzeczy, a rzeczy, które robią, powinny być nazwami klas.

Wyobraź sobie, że stworzyłeś kod, który zajmuje się jednostkami i konwersją jednostek. Możesz nazwać to Utilityi być znienawidzonym przez swoich kolegów; lub możesz to nazwać Unitsi UnitsConversion.

Arseni Mourzenko
źródło
7
„Klasy narzędzi są z natury złe”. Ciekawy. Jakie proponujesz rozwiązanie dla czegoś takiego jak klasa użyteczności funkcji matematycznych? Czy wystąpienie obiektu Math powinno być naprawdę konieczne do korzystania z funkcji wykraczających poza podstawowe operatory arytmetyczne?
FrustratedWithFormsDesigner
1
Zgadzam się z tobą, ale co sugerujesz jako alternatywę?
użytkownik
2
Będę to
nazywać
4
Długo opóźniłem tworzenie biblioteki „utils”, ale ostatecznie trzeba się poddać. Alternatywą jest posiadanie bibliotek DLL składających się z około 3 linii kodu, co jest po prostu kłopotliwe (i przypomina mi bałagan zależności, że node.js to ) lub kopiowanie i wklejanie metod narzędziowych do każdej klasy, która może ich potrzebować (aby uniknąć posiadania tej klasy „kolekcji metod losowych”). IMO to w końcu zło konieczne.
Matti Virkkunen,
3
@FrustratedWithFormsDesigner: po prostu ... Math? Nie rozumiem, jak dodanie Utilitysufiksu do wszystkiego ułatwiłoby nam życie. Czy podczas korzystania z System.Math.Min()metody .NET wolisz pisać SystemUtility.MathUtility.Min()? We wszystkich przypadkach mocno zredagowałem swoją odpowiedź, więc powinna być teraz jaśniejsza.
Arseni Mourzenko