Wydaje mi się, że to kolejne pytanie o kodowanie na stałe i najlepsze praktyki. Powiedzmy, że mam listę wartości, powiedzmy owoców, przechowywanych w bazie danych (musi znajdować się w bazie danych, ponieważ tabela jest używana do innych celów, takich jak raporty SSRS), z identyfikatorem:
1 Apple
2 Banana
3 Grapes
Mogę przedstawić je użytkownikowi, on wybiera jeden, zostanie on zapisany w jego profilu jako FavouriteFruit, a identyfikator przechowywany w jego rekordzie w bazie danych.
Jeśli chodzi o reguły biznesowe / logikę domeny, jakie są zalecenia dotyczące przypisywania logiki do określonych wartości. Powiedz, jeśli użytkownik wybrał opcję Winogrona, chcę wykonać jakieś dodatkowe zadanie, jaki jest najlepszy sposób odniesienia wartości Winogrona:
// Hard coded name
if (user.FavouriteFruit.Name == "Grapes")
// Hard coded ID
if (user.FavoriteFruit.ID == 3) // Grapes
// Duplicate the list of fruits in an enum
if (user.FavouriteFruit.ID == (int)Fruits.Grapes)
albo coś innego?
Ponieważ oczywiście aplikacja FavouriteFruit będzie używana w całej aplikacji, lista może być dodawana lub edytowana.
Ktoś może zdecydować, że chce zmienić nazwę „Winogrona” na „Winogrono”, co oczywiście złamałoby opcję napisanego na stałe.
Zaszyfrowany identyfikator nie jest do końca jasny, jak pokazano, można po prostu dodać komentarz, aby szybko zidentyfikować, który to element.
Opcja wyliczania polega na duplikowaniu danych z bazy danych, co wydaje się nieprawidłowe, ponieważ może się nie zsynchronizować.
W każdym razie z góry dziękujemy za wszelkie uwagi lub sugestie.
MyApplication.Grape.ID
to jąkanie, że tak powiem. „Apple” nie jest „Red_Apple”, podobnie jak identyfikator 3, wynosi również 4. Zatem możliwość zmiany nazwy „Apple” na „Red_Apple” nie ma większego sensu niż deklarowanie, że 3 to 4 (a może nawet 3). Celem wyliczenia jest wyodrębnienie jego numerycznego DNA. Może więc nadszedł czas, aby naprawdę oddzielić dowolne relacyjne klucze DB, które dosłownie nie mają znaczenia w modelach biznesowych.Odpowiedzi:
Za wszelką cenę unikaj ciągów i magicznych stałych. Są całkowicie wykluczone, nie należy ich nawet uważać za opcje. Wydaje się, że pozostawia Ci tylko jedną realną opcję: identyfikatory, czyli znaki wyliczeniowe. Jest jednak jeszcze jedna opcja, która moim zdaniem jest najlepsza. Nazwijmy tę opcję „Wstępnie załadowanymi obiektami”. Dzięki wstępnie załadowanym obiektom możesz wykonać następujące czynności:
To, co się właśnie wydarzyło, polega na tym, że oczywiście załadowałem cały wiersz
Grape
do pamięci, więc mam jego identyfikator gotowy do użycia w porównaniach. Jeśli używasz mapowania obiektowo-relacyjnego (ORM), wygląda to jeszcze lepiej:(Dlatego nazywam to „Wstępnie załadowanymi obiektami”).
Dlatego podczas uruchamiania ładuję wszystkie moje tabele „wyliczania” (małe tabele, takie jak dni tygodnia, miesiące roku, płcie itp.) Do głównej klasy domeny aplikacji. Ładuję je według nazwy, ponieważ oczywiście
MyApplication.Grape
muszę otrzymać wiersz o nazwie „Winogrono” i zapewniam, że każdy z nich został znaleziony. Jeśli nie, mamy gwarantowany błąd w czasie wykonywania podczas uruchamiania, który jest najmniej złośliwy ze wszystkich błędów w czasie wykonywania.źródło
Grape = fetchRow( Fruit.class, NameColumn, "Grape" );
A jeśli zrób coś niepoprawnie,AssertionError
da ci znać.enum
magicznym ciągiem. Chodzi o to, aby skupić wszystkie wiązania według nazwy w jednym miejscu , sprawdzić je podczas uruchamiania i zapewnić bezpieczeństwo typu .Sprawdzanie ciągu znaków jest najbardziej czytelne, ale spełnia podwójną funkcję: jest używane zarówno jako identyfikator, jak i opis (który może się zmienić z niepowiązanych powodów).
Zwykle dzielę oba obowiązki na osobne pola:
Gdzie opis może się zmienić (ale nie „Winogrona” na „Banan”), ale kod nigdy nie może się zmienić.
Chociaż dzieje się tak głównie dlatego, że nasze identyfikatory są prawie zawsze generowane automatycznie, a zatem nie są dobrze dopasowane. Jeśli możesz swobodnie wybierać identyfikatory, być może możesz zagwarantować, że są one zawsze prawidłowe i używają ich.
Jak często ktoś tak naprawdę edytuje „Winogrona” do „Winogrona”? Może nic z tego nie jest konieczne.
źródło
Oczekuje się tutaj, że logika programowania będzie automatycznie dostosowywana do zmieniających się danych. Proste opcje statyczne, takie jak Enum, nie działają tutaj, ponieważ nie można w sposób wykonalny dodać dodatkowych wyliczeń w środowisku wykonawczym.
Kilka wzorów, które widziałem:
Zasadniczo podoba mi się to, że dane są kompletne, jeśli chodzi o odnoszące się do działań, które implikują - nawet jeśli same działania mogą być realizowane gdzie indziej. Każdy kod określający działania niezależne od danych właśnie podzielił twoją reprezentację danych, która najprawdopodobniej będzie się rozbierać i prowadzić do błędów.
źródło
Przechowywanie ich w obu miejscach (w tabeli i w ENUM) nie jest takie złe. Rozumowanie jest następujące:
Przechowując je w tabeli bazy danych, możemy wymusić integralność referencyjną w bazie danych za pomocą kluczy obcych. Kiedy więc kojarzysz osobę lub jakąkolwiek istotę z owocem, w tabeli bazy danych jest tylko owoc.
Przechowywanie ich jako ENUM ma również sens, ponieważ możemy pisać kod bez magicznych ciągów, dzięki czemu kod jest bardziej czytelny. Tak, muszą być zsynchronizowane, ale tak naprawdę, jak trudno byłoby dodać wiersz do ENUM i nową instrukcję insert do bazy danych.
Po zdefiniowaniu ENUM nie zmieniaj jego wartości. Na przykład, jeśli miałeś:
NIE zmieniaj nazwy winogron na winogrona. Po prostu dodaj nowy ENUM.
Jeśli musisz przeprowadzić migrację danych, zastosuj aktualizację, aby przenieść wszystkie winogrona do winogron.
źródło
Masz rację, zadając to pytanie, właściwie to miłe pytanie, gdy próbujesz się bronić przed oceną niedokładnych warunków.
To powiedziawszy, ocena (twoje
if
warunki) niekoniecznie musi koncentrować się na tym, jak sobie z tym poradzić. Zamiast tego zwróć uwagę na sposób propagowania zmian, które spowodowałyby problem z brakiem synchronizacji.Podejście strunowe
Jeśli musisz używać ciągów, dlaczego nie udostępnić funkcji zmiany listy za pomocą interfejsu użytkownika? Zaprojektować system tak, że po zmianie
Grape
naGrapes
, na przykład, zaktualizować wszystkie rekordy obecnie przedstawieniuGrape
.Podejście ID
Zawsze wolałbym odwoływać się do identyfikatora, pomimo kompromisu w zakresie jego czytelności.
The list may be added to
może być coś, o czym zostaniesz powiadomiony, jeśli ujawnisz taką funkcję interfejsu użytkownika. Jeśli martwisz się zmianą kolejności elementów zmieniających identyfikator, ponownie propaguj taką zmianę do wszystkich zależnych rekordów. Podobnie jak powyżej. Inną opcją (zgodnie z właściwą konwencją normalizacyjną byłoby posiadanie kolumny wyliczeniowej / id - i odwołanie się do bardziej szczegółowejFruitDetail
tabeli, która zawiera kolumnę „Zamówienie”, którą można wyszukać).Tak czy inaczej, możesz zobaczyć, proponuję kontrolować zmianę lub aktualizację listy. To, czy robisz to za pomocą ORM, czy innego dostępu do danych, zależy od specyfiki Twojej technologii. To, co zasadniczo robisz, wymaga od ludzi, aby odeszli od DB dla takich zmian - co moim zdaniem jest w porządku. Większość głównych CRM będzie spełniać te same wymagania.
źródło
Bardzo częsty problem. Podczas gdy powielanie po stronie klienta danych może wydawać się naruszać zasady DRY , jest tak naprawdę z powodu różnicy w paradygmacie między warstwami.
Poza tym wyliczenie (lub cokolwiek innego) z bazą danych nie jest tak rzadkie. Być może wypchnąłeś inną wartość do tabeli metadanych w celu obsługi nowej funkcji raportów, która nie jest jeszcze używana w kodzie klienta.
Czasami dzieje się to również w drugą stronę. Nowa wartość wyliczana jest dodawana po stronie klienta, ale aktualizacja bazy danych nie może nastąpić, dopóki DBA nie zastosuje zmian.
źródło
Zakładając, że mówimy o tym, co jest zasadniczo statycznym wyszukiwaniem, to trzecia opcja - wyliczenie - jest w zasadzie jedynym rozsądnym wyborem. To, co zrobiłbyś, gdyby baza danych nie była zaangażowana, więc ma to sens.
Następnie pojawia się pytanie o to, jak zsynchronizować wyliczenia i tabele statyczne / odnośniki w bazie danych, i niestety nie jest to problem, na który jeszcze mam pełną odpowiedź.
Z wyboru wykonuję całą konserwację schematu w kodzie i dlatego mogę zachować relację między kompilacją aplikacji a oczekiwaną wersją schematu, więc łatwo jest zsynchronizować wyszukiwanie i wyliczanie, ale należy pamiętać o tym, aby zrobić. Byłoby lepiej, gdyby był bardziej zautomatyzowany (a także automatyczny test integracji w celu upewnienia się, że wyliczenia i wyszukiwania pasują do siebie), ale nigdy tego nie wdrożyłem.
źródło