Problemy z unikaniem klas Naming Smurf z przestrzeniami nazw

39

Wyciągnąłem stąd termin nazywanie smerfa (numer 21). Aby zaoszczędzić każdemu, kto nie zna problemu, nazewnictwo Smurf polega na prefiksowaniu szeregu powiązanych klas, zmiennych itp. Wspólnym przedrostkiem, aby uzyskać „a SmurfAccountViewprzechodzi SmurfAccountDTOna SmurfAccountController” itd.

Rozwiązaniem, o którym ogólnie słyszałem, jest utworzenie przestrzeni nazw smurf i upuszczenie przedrostków smurf. To ogólnie mi dobrze służyło, ale mam dwa problemy.

  1. Pracuję z biblioteką z Configurationklasą. Można go było nazwać, WartmongerConfigurationale znajduje się w przestrzeni nazw Wartmonger, więc po prostu się nazywa Configuration. Mam również Configurationklasę, którą można nazwać SmurfConfiguration, ale znajduje się ona w przestrzeni nazw Smurf, więc byłoby to zbędne. W moim kodzie są miejsca, w których Smurf.Configurationpojawia się obok, Wartmonger.Configurationa wpisywanie w pełni kwalifikowanych nazw jest nieporęczne i sprawia, że ​​kod jest mniej czytelny. Lepiej byłoby poradzić sobie z SmurfConfigurationa (gdyby to był mój kod, a nie biblioteka) WartmongerConfiguration.

  2. Mam klasę nazwaną Servicew mojej przestrzeni nazw Smurf, którą można by nazwać SmurfService. Servicejest fasadą na złożonej bibliotece Smurf, która obsługuje zadania Smurf. SmurfServicewydaje się lepszą nazwą, ponieważ Servicebez przedrostka Smurf jest tak niesamowicie ogólny. Mogę zaakceptować, że SmurfServicebyła to już ogólna, bezużyteczna nazwa, a zabranie smerfa ​​tylko to uwidoczniło. Ale to mogło być nazwane Runner, Launcheritp i nadal będą „czuć się lepiej” mi się SmurfLauncher, bo nie wiem, co Launcherrobi, ale wiem, że to, co SmurfLauncherrobi. Można argumentować, że to, co Smurf.Launcherrobi, powinno być tak samo oczywiste jakSmurf.SmurfLauncher, ale widziałem, że `Smurf.Launcher jest rodzajem klasy związanej z konfiguracją, a nie klasą, która uruchamia smerfy.

Jeśli istnieje otwarty i zamknięty sposób radzenia sobie z jednym z nich, byłoby świetnie. Jeśli nie, jakie są typowe praktyki ograniczania ich irytacji?

Daniel Koverman
źródło
3
Czy Smurf.Launcheruruchamia smerfy, czy uruchamia SmurfJobs? Być może można to nazwać Smurf.JobLauncher?
Blorgbeard
2
Jest to zapachowy kod określający klasę XService, XManager itp. To nic nie znaczy. To jest jak Util. Jeśli przeglądasz nazwy plików, wszystko może tam być lub może nie być. Nie ma sposobu, aby wiedzieć, chyba że zajrzysz do środka. Zmieniłbym nazwę z SmurfService na coś zupełnie innego.
Daniel Kaplan
1
W rzeczywistości uruchamia SmurfJobs lub technicznie uruchamia je, aby były zgodne z językiem dokumentacji Smurf. W świetle tej i innych odpowiedzi zamierzam zmienić nazwę SmurfServicena SmurfJobRunner. Wydaje się, że numer 1 nie ma najlepszej agnostycznej rozdzielczości językowej, jak się spodziewałem. Widzę przypadki, w których pójście SmurfConfigurationbyłoby właściwym telefonem, ale w moim przypadku myślę, że Configurationnajlepiej nawet z kłopotami Wartmonger.Configuration.
Daniel Koverman
6
Staram się zrozumieć, dlaczego masz jedną klasę, która dba o konfigurację zarówno Wartmongerów, jak i Smerfów.
Donal Fellows
Dlaczego Smurf.Configurationi SmurfConfigurationczujesz się inaczej? Z pewnością to nie jest dodatkowy char, prawda? (Skróć do, Configjeśli problemem jest długość.) Czy Smurf.Configurationmasz jakieś problemy, które SmurfConfigurationnie?
Pablo H

Odpowiedzi:

16

Podnosisz dobre punkty.

  1. Jeśli chodzi o posiadanie duplikatów klas, możesz aliasować klasy w języku C #. Użyj na przykład using ColorScheme = The.Fully.Qualified.Namespace.Outlook2007ColorScheme;Zobacz ten post na StackOverflow . Nie wskazałeś swojego języka programowania, ale wywnioskowałem go z tego, co napisałeś. Jeśli więc masz do czynienia z dwoma różnymi projektami, możesz je aliasować jako SmurfConfigurationi, WartmongerConfigurationco uwolniłoby dwuznaczność podczas konsumowania obu klas.

  2. Ponieważ usługa jest narażona na zewnętrzne aplikacje, nie widzę problemu z oznakowaniem usługi nazwą Twojej aplikacji, więc w tym przypadku SmurfServicebyłaby ważna, ponieważ w rzeczywistości ujednolicałaby grupy usług w korzystającej aplikacji.

Uważam, że należy używać przestrzeni nazw, aby uniknąć tego stylu nazywania. Utrudnia to przeglądanie kodu i zobaczenie, czym jest klasa bez czytania MyCompanyMyProductMyAreaClassName. Korzystanie z techniki aliasingu pozwala zmniejszyć niejednoznaczność w razie potrzeby. Myślę, że powinieneś jedynie wprowadzić złożoność do nazewnictwa, jak wskazałem w punkcie 2, kiedy ludzie będą korzystać z usługi. W tym przypadku ma to sens, aby mieć taki styl nazewnictwa, ponieważ jeśli konsument ma wiele usług, konsumuje dwuznaczność może być myląca.

Sam
źródło
5
aliasy po prostu mylą. Zamiast Smurf.Service masz teraz SmurfService = Smurf.Service. Więc równie dobrze mógłbyś po prostu mieć SmurfService jako nazwę rzeczy na pierwszym miejscu. Mają miejsce, ale nie dla tego konkretnego problemu. Jest to jednak prawdopodobnie najlepsza odpowiedź na problem, na który nie ma odpowiedzi :)
gbjbaanb
C # we mnie pojawiło się w moim pytaniu, ale aktualnie mam do czynienia z Javą i org.apache.smurfville.wartmonger.configuration. To niestety wyklucza pseudonimy. 2 to solidny punkt, więc zamierzam zachować markę Smurf dla Usługi.
Daniel Koverman
25

Istotą przestrzeni nazw jest to, że możesz mieć klasy o tej samej nazwie z różnych bibliotek bez kolizji. Gdy musisz użyć tej samej nazwanej klasy z obu, musisz usunąć niejednoznaczność, poprzedzając jedną lub obie z ich zakresem przestrzeni nazw.

To powiedziawszy, nie jest tak źle mieć wiele klas Smurf, jeśli Smurf mówi ci coś konkretnego o klasie. Nazwy klas powinny być wystarczająco opisowe, aby dać ci trochę informacji o tym, co robi klasa.

      Session
       ^   ^
      /     \
DBSession   HttpSession

Podobnie a DBSessionmoże wziąć DBRequestobiekt, który zwraca DBResponseobiekt. HttpSessionMoże również działać na HttpRequesti HttpResponseobiekty.

Są to zajęcia Smerfowe z przeznaczeniem.

Mogą żyć w MyCompanyprzestrzeni nazw, ale MyCompanyHttpSessioni MyCompanyDBSessionnie daje żadnych więcej informacji, niż przedtem. W takim przypadku upuść Smerfa ​​i uczyń go przestrzenią nazw.

MyCompany.HttpSession
Dave Rager
źródło
3

Wcześniej spotkałem się z tym samym zamieszaniem i zwykle jest to naprawdę kwestia tego, czy uwzględniamy coś, co jest częścią nazwy.

Wymieniasz SmurfConfigurationi WartmongerConfigurationjako potencjalne rodzaje konfiguracji. Wskazujesz, że usunąłeś przymiotnik (jego rodzaj) w przestrzeni nazw, aby pozostała tylko wanilia Configuration. Unikałbym tego.

To tak, jakby zdecydować, że lody truskawkowe to po prostu lody w przestrzeni nazw truskawek i podobnie z czekoladą, ale zdarzyło się, że oddzieliłeś przymiotnik, który nadaje mu tożsamość z samą rzeczą. To nie są lody w kategorii truskawek. To lody truskawkowe - rodzaj lodów.

Wyobraźmy sobie, że w aplikacji zaimportujesz Strawberry.IceCreamklasę, a następnie zaczniesz tworzyć instancję bezpośrednio z IceCream.

var ic = new IceCream(); //actually I'm strawberry ice cream

Może się to wydawać dobre i dobre, dopóki nie skończysz importować innej IceCreamklasy. Teraz wracasz do pierwotnego problemu konieczności rozróżnienia między nimi, co jest problematyczne. Przez cały czas chciałeś:

var sic = new StrawberryIceCream();
var cic = new ChocolateIceCream();

Lepiej pozostawić przestrzenie nazw, aby uniknąć potencjalnych konfliktów między stronami trzecimi, które mogą nawiązywać do tych samych pojęć w swoich bibliotekach. Gdy jednak jeden programista tworzy bibliotekę lub projekt, powinien nazwać każdą koncepcję indywidualnie i używać przestrzeni nazw jako folderów tylko dla organizacji. Często nazwa folderu znajduje się w nazwie pojęć, które organizuje i jest w porządku.

Mario T. Lanza
źródło
2

Zdecydowanie dobrą zasadą jest to, że jeśli masz wspólny prefiks na wielu klasach, to prawdopodobnie zasługują na to, aby pójść we własnej przestrzeni nazw. Aby poradzić sobie z tym problemem, kiedy trzeba użyć klas o podobnej nazwie z dwóch przestrzeni nazw:

1) Aliasuj przestrzenie nazw, choć skracam i do rzeczy, każdy naturalny skrót, może nawet tylko 1 litera:

using Sm = Smurf;
using W = Wartmonger;

Następnie zawsze używaj prefiksu wszędzie tam, gdzie jest używany, i odpowiednio nazywaj wystąpienia:

Sm::Configuration smConf; 
W::Configuration wConf;

2) Aliasuj klasę, jak sugerowano w innej odpowiedzi.

using SmConf = Smurf.Configuration;

3) W dowolnej bibliotece, nad którą sprawujesz kontrolę, nie używaj terminu „Konfiguracja”. Użyj tezaurusa: np. „Ustawienia”, „Model”, „Parametry”. Może i tak może mieć większe znaczenie dla kontekstu: np. Gdyby Smurf był rodzajem modułu analizy numerycznej, napisałbyś, że być może „Parametry” byłyby lepsze dla jego konfiguracji. Korzystaj ze specjalnego słownictwa związanego z kontekstem modułu, aby wymyślić unikalne nazwy, które zachowują wyjątkowość nawet po zmieszaniu z innymi przestrzeniami nazw. Wydaje mi się, że może to być odpowiedź na pytanie 2 OP.

4) Refaktoryzuj kod, abyś nie musiał mieszać użycia konfiguracji z dwóch różnych miejsc. Szczegóły tego zależy od ciebie.

5) Połącz dwie konfiguracje w jedną, zanim przekażesz ją klasie. Użyj połączonej klasy conf do reprezentowania:

struct Conf {
    SmurfConfiguration smurf;
    WartmongerConfiguation wart;
}

Krótkie nazwy zmiennych składowych osiągają teraz to samo, co aliasing przestrzeni nazw / klasy.

Benedykt
źródło
0

Wydaje się dziwne, że dodanie jednego punktu do nazwy przeszkadza.

Wartmonger.Configuration configuration = Wartmonger.Configuration .new();

// vs

WartmongerConfiguration configuration = WartmongerConfiguration.new();

Jeśli oba Smurfi Wartmongerkonfiguracje stosowane razem w jednym miejscu, ale Separatly są one wykorzystywane w wielu miejscach - wtedy nazw jest zdecydowanie dobrym podejściem.

Mając nazw daje możliwość wykorzystania „czystych” nazw w kodzie wewnętrznym, gdzie z prefiksów skończyć się za pomocą SmurfConfigurationwnętrza SmurfService„s kody wewnętrzne, które mogą stać się irytujące za każdym razem otworzysz ten kod.

Fabio
źródło