Pobieram zestaw krotek z bazy danych i umieszczam na mapie. Zapytanie do bazy danych jest kosztowne.
Nie ma oczywistej naturalnej kolejności elementów na mapie, ale kolejność wstawiania ma jednak znaczenie. Sortowanie mapy byłoby ciężką operacją, więc chcę tego uniknąć, biorąc pod uwagę, że wynik zapytania jest już posortowany tak, jak tego chcę. Dlatego właśnie przechowuję wynik zapytania w LinkedHashMap
i zwracam mapę z metody DAO:
public LinkedHashMap<Key, Value> fetchData()
Mam metodę, processData
która powinna wykonać pewne przetwarzanie na mapie - modyfikując niektóre wartości, dodając nowe klucze / wartości. Jest zdefiniowany jako
public void processData(LinkedHashMap<Key, Value> data) {...}
Jednak kilka linterów (Sonar itp.) Narzeka, że typem „danych” powinien być interfejs, taki jak „Mapa”, a nie implementacja „LinkedHashMap” ( kałamarnica S1319 ).
Więc w zasadzie mówi, że powinienem był
public void processData(Map<Key, Value> data) {...}
Chcę jednak, aby sygnatura metody mówiła, że kolejność map ma znaczenie - ma znaczenie dla algorytmu w processData
- dzięki czemu moja metoda nie jest przekazywana po prostu losową mapą.
Nie chcę używać SortedMap
, ponieważ to (z javadoc zjava.util.SortedMap
) „jest uporządkowane zgodnie z naturalną kolejnością jego kluczy lub przez komparator zwykle dostarczany w czasie tworzenia posortowanej mapy”.
Moje klucze nie mają naturalnej kolejności , a tworzenie Komparatora, który nic nie robi, wydaje się pełne.
I nadal chciałbym, aby była to mapa, aby skorzystać z put
unikania duplikatów kluczy itp. Jeśli nie, data
mogłaby to być List<Map.Entry<Key, Value>>
.
Jak więc powiedzieć, że moja metoda chce mapy, która jest już posortowana ? Niestety nie ma java.util.LinkedMap
interfejsu, inaczej bym go użył.
źródło
if you are new to programming and stumble upon this answer, don't think this allows you to go against best practice because it doesn't.
- Dobra rada, jeśli istnieje coś takiego jak „najlepsza praktyka”. Lepsza rada: dowiedz się, jak podejmować właściwe decyzje. Postępuj zgodnie z praktyką, jeśli ma to sens, ale pozwól narzędziom i władzom kierować procesem myślenia, a nie dyktować.Walczysz z trzema rzeczami:
Pierwszą jest biblioteka kontenerów Java. Nic w jego taksonomii nie pozwala ci ustalić, czy klasa iteruje w przewidywalnym porządku. Nie ma
IteratesInInsertedOrderMap
interfejsu, który mógłby zostać zaimplementowanyLinkedHashMap
, co uniemożliwia sprawdzenie typu (i użycie alternatywnych implementacji, które zachowują się w ten sam sposób). Prawdopodobnie jest to zgodne z projektem, ponieważ jego duch polega na tym, że naprawdę powinieneś być w stanie poradzić sobie z obiektami, które zachowują się jak abstrakcjaMap
.Drugi to przekonanie, że to, co mówi twój liniowiec, musi być traktowane jak ewangelia i że ignorowanie wszystkiego, co mówi, jest złe. W przeciwieństwie do tego, co w dzisiejszych czasach uchodzi za dobrą praktykę, ostrzeżenia liniowe nie powinny stanowić przeszkody w dobrym nazywaniu kodu. Są monitami o uzasadnienie kodu, który napisałeś, i wykorzystują twoje doświadczenie i osąd, aby ustalić, czy ostrzeżenie jest uzasadnione. Nieuzasadnione ostrzeżenia powodują, że prawie każde narzędzie do analizy statycznej zapewnia mechanizm informujący, że sprawdziłeś kod, uważasz, że to, co robisz, jest w porządku i że nie powinni narzekać na to w przyszłości.
Po trzecie, i to jest prawdopodobnie jego główne znaczenie,
LinkedHashMap
może być niewłaściwym narzędziem do tego zadania. Mapy są przeznaczone do losowego, niezamówionego dostępu. JeśliprocessData()
po prostu iteruje się po rekordach w kolejności i nie musi znajdować innych rekordów według klucza, wymusza się konkretną implementacjęMap
wykonania zadaniaList
. Z drugiej strony, jeśli potrzebujesz obu,LinkedHashMap
jest to właściwe narzędzie, ponieważ wiadomo, że możesz robić to, co chcesz, i jest to więcej niż uzasadnione.źródło
OrderedMap
, równie dobrze mogę powiedziećUniqueList
. Tak długo, jak jest to jakaś kolekcja ze zdefiniowaną kolejnością iteracji, która zastępuje duplikaty przy wstawianiu.Set
tylko klucze podczas budowania listy jako sposób na ich wykrycie.processData
modyfikuje mapę, zastępując niektóre wartości, wprowadzając nowe klucze / wartości.processData
Mogłoby więc wprowadzić duplikaty, gdyby działało na czymś innym niżMap
.UniqueList
(lubOrderedUniqueList
) i użyć go. Jest to dość łatwe i sprawia, że zamierzone użytkowanie jest jaśniejsze.Jeśli wszystko, co dostajesz,
LinkedHashMap
to możliwość zastępowania duplikatów, ale tak naprawdę używasz go jakoList
, to sugeruję, aby lepiej zakomunikować to użycie z własną niestandardowąList
implementacją. Można oprzeć się na istniejących zbiorów klasy Java i po prostu zastąpić dowolnyadd
iremove
metod, aby zaktualizować swój sklep podkładową i śledzić klucza, aby zapewnić niepowtarzalność. Nadanie tej charakterystycznej nazwy, jak,ProcessingList
pozwoli wyjaśnić, że argumenty przedstawione twojejprocessData
metodzie muszą być traktowane w określony sposób.źródło
ProcessingList
jako aliasLinkedHashMap
- zawsze możesz zdecydować o zastąpieniu go innym czymś później, o ile nie zmienisz publicznego interfejsu.Słyszę, jak mówisz: „Mam jedną część mojego systemu, która produkuje LinkedHashMap, a w innej części mojego systemu muszę akceptować tylko obiekty LinkedHashMap, które zostały wytworzone przez pierwszą część, ponieważ te wytworzone w innym procesie wygrały” t działa poprawnie. ”
To sprawia, że myślę, że problem polega na tym, że próbujesz użyć LinkedHashMap, ponieważ w większości pasuje on do poszukiwanych danych, ale w rzeczywistości nie można go zastąpić żadną inną instancją niż te, które tworzysz. To, co naprawdę chcesz zrobić, to stworzyć własny interfejs / klasę, którą tworzy twoja pierwsza część, a druga część zużywa. Może owinąć „prawdziwą” LinkedHashMap i udostępnić narzędzie do pobierania map lub implementować interfejs mapy.
To trochę różni się od odpowiedzi CandiedOrange, ponieważ zalecałbym enkapsulację prawdziwej mapy (i delegowanie wywołań do niej w razie potrzeby) zamiast jej rozszerzania. Czasami jest to jedna z tych świętych wojen w stylu, ale na pewno brzmi dla mnie, że nie jest to „Mapa z dodatkowymi rzeczami”, ale „Moja torba z przydatnymi informacjami o stanie, którą wewnętrznie mogę reprezentować z Mapą”.
Gdybyście mieli dwie zmienne, które musielibyście przekazać w ten sposób, prawdopodobnie stworzylibyście dla niej klasę, nie zastanawiając się nad tym dużo. Ale czasem warto mieć klasę, nawet jeśli jest to tylko jedna zmienna składowa, tylko dlatego, że jest to logicznie to samo, a nie „wartość”, ale „wynik mojej operacji, z którą muszę zrobić później”.
źródło
MyBagOfUsefulInformation
musiałby metodę (lub konstruktora), aby go zapełnić:MyBagOfUsefulInformation.populate(SomeType data)
. Aledata
musiałby to być wynik posortowanego zapytania. Co bySomeType
było, gdyby nieLinkedHashMap
? Nie jestem pewien, czy uda mi się przełamać ten Catch 22.MyBagOfUsefulInformation
zostać utworzony przez DAO lub cokolwiek generującego dane w twoim systemie? Dlaczego w ogóle musisz udostępniać mapę pod spodem reszcie kodu poza producentem i konsumentem torby?MyBagOfUsefulInformation
jako parametr metodę DAO: softwareengineering.stackexchange.com/a/360079/52573LinkedHashMap to jedyna mapa Java, która ma funkcję kolejności wstawiania, której szukasz. Dlatego odrzucenie zasady inwersji zależności jest kuszące, a może nawet praktyczne. Najpierw jednak zastanów się, co trzeba zrobić, aby to zrobić. Oto, o co proszą SOLID .
Uwaga: zastąp nazwę nazwą
Ramdal
opisową, która informuje, że konsument tego interfejsu jest właścicielem tego interfejsu. Co sprawia, że to organ decyduje, czy kolejność wstawiania jest ważna. Jeśli po prostu to nazwieszInsertionOrderMap
, naprawdę nie rozumiesz sensu.Czy to duży projekt z przodu? Może zależy to od tego, jak prawdopodobne jest, że kiedykolwiek będziesz potrzebować implementacji
LinkedHashMap
. Ale jeśli nie stosujesz DIP tylko dlatego, że byłby to ogromny ból, nie sądzę, aby płyta kotła była bardziej bolesna niż ta. Jest to wzorzec, którego używam, gdy chcę, aby kod nietykalny zaimplementował interfejs, którego nie ma. Najbardziej bolesne jest myślenie o dobrych imionach.źródło
Dzięki za wiele dobrych sugestii i jedzenie do namysłu.
Ostatecznie rozszerzyłem tworzenie nowej klasy mapy, tworząc
processData
metodę instancji:Następnie dokonałem refaktoryzacji metody DAO, aby nie zwracała mapy, ale zamiast tego przyjmuje
target
mapę jako parametr:Więc wypełnianie
DataMap
i przetwarzanie danych jest teraz procesem dwuetapowym, co jest w porządku, ponieważ istnieją inne zmienne, które są częścią algorytmu, który pochodzi z innych miejsc.To pozwala mojej implementacji mapy kontrolować sposób wstawiania do niej wpisów i ukrywa wymagania dotyczące zamawiania - jest to teraz szczegół implementacji
DataMap
.źródło
Jeśli chcesz poinformować, że użyta struktura danych istnieje z jakiegoś powodu, dodaj komentarz powyżej podpisu metody. Jeśli inny programista w przyszłości natrafi na ten wiersz kodu i zauważy ostrzeżenie dotyczące narzędzia, może również zauważyć komentarz i powstrzymać się od „naprawienia” problemu. Jeśli nie ma komentarza, nic nie powstrzyma ich przed zmianą podpisu.
Moim zdaniem tłumienie ostrzeżeń jest gorsze niż komentowanie, ponieważ samo tłumienie nie podaje powodu, dla którego ostrzeżenie zostało zniesione. Kombinacja wyłączenia ostrzeżenia i komentarza również będzie w porządku.
źródło
Pozwól mi więc spróbować zrozumieć twój kontekst tutaj:
Teraz to, co już robisz:
A oto twój obecny kod:
Moją sugestią jest wykonanie następujących czynności:
Przykład kodu
Wydaje mi się, że pozbyłoby się to ostrzeżenia Sonaru, a także określił w podpisie specyficzny układ danych wymaganych przez metodę przetwarzania.
źródło
MyTupleRepository
zostanie stworzony?)To pytanie jest w rzeczywistości wiązką problemów z połączonym modelem danych. Musisz zacząć je rozplątywać pojedynczo. Bardziej naturalne, intuicyjne rozwiązania znikną, gdy spróbujesz uprościć każdy element układanki.
Problem 1: Nie możesz polegać na zamówieniu DB
Twoje opisy sortowania danych nie są jasne.
ORDER BY
klauzuli. Jeśli nie jesteś, ponieważ wydaje się to zbyt drogie, twój program ma błąd . Bazy danych mogą zwracać wyniki w dowolnej kolejności, jeśli nie zostanie określona; nie możesz polegać na tym, że przypadkowo zwraca dane w kolejności tylko dlatego, że uruchomiłeś zapytanie kilka razy i tak to wygląda. Kolejność może ulec zmianie, ponieważ wiersze są przestawiane na dysku lub niektóre są usuwane, a nowe zajmują miejsce lub dodawany jest indeks. Państwo musi określićORDER BY
klauzulę jakiegoś rodzaju. Prędkość jest bezwartościowa bez poprawności.ORDER BY
klauzuli. W przeciwnym razie masz błędy. Jeśli taka kolumna jeszcze nie istnieje, musisz ją dodać. Typowe opcje dla takich kolumn to kolumna ze znacznikiem czasu wstawiania lub klucz automatycznego zwiększania. Klucz automatycznego zwiększania jest bardziej niezawodny.Problem 2: Wydajne sortowanie w pamięci
Po upewnieniu się, że gwarantowane jest zwracanie danych w oczekiwanej kolejności, możesz wykorzystać ten fakt, aby sortowanie w pamięci było znacznie wydajniejsze. Wystarczy dodać kolumnę
row_number()
lubdense_rank()
(lub odpowiednik bazy danych) do zestawu wyników zapytania. Teraz każdy wiersz ma indeks , który da ci bezpośrednie wskazanie, jaka powinna być kolejność, i możesz sortować według tego trywialnie w pamięci. Upewnij się tylko, że nadasz indeksowi znaczącą nazwę (npsortedBySomethingIndex
.).Altówka. Teraz nie musisz już polegać na kolejności zestawów wyników bazy danych.
Problem 3: Czy w ogóle potrzebujesz tego przetwarzania w kodzie?
SQL jest naprawdę bardzo wydajny. To niesamowity deklaratywny język, który pozwala na wiele przekształceń i agregacji danych. Większość DB obsługuje obecnie nawet operacje między wierszami. Nazywa się je funkcjami okna lub analitycznymi:
OVER
Klauzula SQL Server dla funkcji oknaCzy w ogóle potrzebujesz wciągnąć swoje dane do pamięci? Czy możesz wykonać całą pracę w zapytaniu SQL, używając funkcji okna? Jeśli możesz wykonać całą (a może nawet znaczącą część) pracę w DB, to fantastycznie! Twój problem z kodem zniknął (lub stał się o wiele prostszy)!
Problem 4: Co robisz
data
?Zakładając, że nie możesz zrobić tego wszystkiego w DB, pozwól mi to wyjaśnić. Bierzesz dane jako mapę (na którą składają się rzeczy, których nie chcesz sortować), a następnie iterujesz je w kolejności wstawiania i modyfikujesz mapę w miejscu, zastępując wartość niektórych kluczy i dodając nowe?
Przepraszam, ale co do cholery?
Dzwoniący nie powinni się o to martwić . System, który stworzyłeś, jest wyjątkowo delikatny. Wystarczy jeden głupi błąd (może nawet popełniony przez ciebie, tak jak wszyscy to zrobiliśmy), aby wprowadzić jedną małą złą zmianę, a cała sprawa zapada się jak talia kart.
Oto może lepszy pomysł:
List
.Możliwą odmianą może być zbudowanie posortowanej reprezentacji, a następnie utworzenie mapy klucza do indeksu . Umożliwi to zmodyfikowanie posortowanej kopii w miejscu, bez przypadkowego tworzenia duplikatów.
A może ma to bardziej sens: pozbyć się
data
parametru iprocessData
faktycznie pobrać własne dane. Następnie możesz udokumentować, że to robisz, ponieważ ma bardzo specyficzne wymagania dotyczące sposobu pobierania danych. Innymi słowy, spraw, aby funkcja była właścicielem całego procesu, a nie tylko jednego jego fragmentu; wzajemne zależności są zbyt silne, aby podzielić logikę na mniejsze części. (Zmień nazwę funkcji w tym procesie.)Może to nie zadziała w twojej sytuacji. Nie wiem bez pełnych szczegółów problemu. Ale znam kruchy i mylący projekt, kiedy go słyszę.
Podsumowanie
Myślę, że problemem tutaj jest to, że diabeł tkwi w szczegółach. Kiedy zaczynam mieć takie kłopoty, zwykle dzieje się tak dlatego, że mam nieodpowiednią reprezentację moich danych dla problemu, który próbuję rozwiązać. Najlepszym rozwiązaniem jest znalezienie lepszej reprezentacji , a wtedy mój problem staje się prosty (może nie łatwy, ale bezpośredni) do rozwiązania.
Znajdź kogoś, kto osiągnie ten punkt: Twoim zadaniem jest zredukowanie problemu do zestawu prostych, prostych. Następnie możesz zbudować solidny, intuicyjny kod. Porozmawiaj z nimi. Dobry kod i dobry design sprawiają, że myślisz, że każdy idiota mógł je wymyślić, ponieważ są one proste. Może jest jakiś starszy programista, który ma sposób myślenia, z którym możesz porozmawiać.
źródło
select key, value from table where ... order by othercolumn
i musi zachować kolejność przetwarzania. Kolejność wstawiania oni powołując się na to kolejność wkładania ich mapy , określonej według kolejności stosowanej w ich zapytania, a nie kolejności wstawiania do bazy danych . Jest to oczywiste, ich wykorzystaniaLinkedHashMap
, który jest strukturą danych, która ma cechy zarówno oMap
ioList
par klucz-wartość.order by
klauzula, ale nie jest ona trywialna ( nie tylkoorder by column
), więc chcę uniknąć ponownego sortowania w Javie. Chociaż SQL jest potężny (a mówimy tutaj o bazie danych Oracle 11g), charakterprocessData
algorytmu znacznie ułatwia wyrażanie w Javie. I tak, „kolejność wstawiania” oznacza „ kolejność wstawiania mapy ”, tj. Kolejność wyników zapytania.