Emum niby prosty jest, a bywa bardzo zdradliwy ;-)
Nie chodzi mi nawet o to, że w Java'ie do wersji 8 dało się stworzyć enuma, który formalnie nie istniał, więc kod:
public enum Toster {
OTHER_TEST(TEST.getS()),
TEST("TEST"),
TEST2("TEST2");
String s;
String getS() {
return s;
}
Toster(String s) {
this.s=s;
}
}
był legalny, ale rzucał NullPointerException... w Java 9 już się nie skompiluje...
Ani o to, że jeszcze wcześniej gdy kolekcje były w rozsypce zdarzał się kod:
var v = new Vector<>();
Enumeration enum = v.elements();
Który w Java 5 przestawał działać bo tam enum to był keyword ;-)
Nie, to są quirky wczesnej Java'y (choć nadal na produkcji jest MASA kodu działającego na JVM 8 ;-))
Chodzi o sam koncept i gdzie go używać.
Bo enum ma to do siebie, że to jest z definicji zamknięta lista. Można ją rozszerzyć, ale to zmienia API tego enuma. I niby nie ma problemu... trochę to psuje serializację, ale mało kto tego potrzebował.... do czasu.
Bo przyszło programowanie sieciowe i cały Internet zaczął pracować w debug mode z JSONem jako formatem do przesyłania danych.
Bardzo szybko programiści wpadli na to, że przecież niektóre z tych Stringów które tam są wysyłane to są enumy i zaczęli robić serializację do JSONa i deserializację z JSONa do obiektów z enumami....
Wczoraj widziałem kolejny team (mój ;-)), który poległ z enumem, choć tym razem w Pythonie.
Jak zwykle mamy deserializację po stronie backendu, która żeby to wszystko ładnie zrobić używa enumów.... i lecie 500 gdy ktoś przyśle coś spoza listy.
Tu jest też ciekawe pytanie czy zmiana kodu by to 500 nie leciało to poprawka błędu, czy nie? ;-)
Bo de facto kod zakładał, że istnieje legalna lista wartości i choć 500 nie jest może najlepszym sposobem na obsługę tego (400 byłoby lepsze) to nadal wołający "złamał" API ;-)
Bo de facto kod zakładał, że istnieje legalna lista wartości i choć 500 nie jest może najlepszym sposobem na obsługę tego (400 byłoby lepsze) to nadal wołający "złamał" API ;-)
Stąd ja twierdzę, że to nie jest bug a new feature ;-)
W innym projekcie, też małżeństwo JSONa i enuma popsuł konwersję bo dostawca danych dodał nowy typ złącza... tu team był oburzony - bo złamali specyfikację - ale ta specyfikacja nigdy nie istniała, to team sobie sam ją napisał (bardzo 2000 like podejście).
Jeszcze wcześniej, znów JSON + Enumy, ale tam jeszcze Avro było gdzieś... i w końcu było tak architekt zmusił nas do używania Avro - bo tak lepiej, a później jak jeden z teamów źle tego użył to uznał, że OK, używajcie JSONa jako transportu ;-) [oryginalny problem był w tym, że JSON nie wspiera naturalnie migracji schemy....]
Jeszcze wcześniej, była sobie biblioteczka annotacji do Java'y, chodziło o Authz ze SpringBootem, tam w oryginalnej (mojej ;-)) wersji były scope'y jako String, ktoś wziął mój kod i uznał, że zrobi to lepiej i użył Enumów... działało do momentu gdy pojawił się nowy scope... który w ogóle nie dotyczył tej aplikacji, ale że ich wersja wymuszała deserializację scope'ów z tokena na enumy to mieli problem ;-)
Zaryzykuję twierdzenie, że enum nigdy nie został stworzony do bycia używanym do łączności między różnym kodem. Zrobiliśmy mu piggyback jakoś zapominając, że schema ma tendencję do zmieniania się i przez to mamy zabawne błędy z rodzaju - zmiana mnie w ogóle nie dotyczy, ale dane z wynikiem tej zmiany mogą mnie popsuć ;-)
Stąd ja tam czytając kod traktuję enum jako taki sygnał code smell (dla mnie code smell to wszystko co jest "niezwykłe", enum, protected (w Java'ie), extend (też w Java'ie), print, catch bez obsługi wyjątku i bez logu, deklaracja szerokich Exception i tak dalej).
Czy używam? Zależy, ale chyba zwykle nie.
Są świetne do maszyny stanów, ale stałe typu int w kodzie też działają, a są nawet bardziej czytelne.
Jest też sens zaryzykowania z enumem w jakieś większej klasie jeśli historia nam pokazała, że to lista jest zamknięta, albo gdy mamy obsługę "unknown"... ciągle to jednak jest ryzykowne bo jak wiele rzeczy da się rozszerzyć gdy pojawi się nowy enum, to nigdy nie masz pewności, że gdzieś nie ma czegoś co zostanie popsute.
Ja bym chyba nie używał, jakoś w moim kodzie nie znalazłem powodów, ale nie zakazuję, daję tylko takie "dwa razy się zastanów czy użyć".
A jeśli to ma być do JSONa to bym prawie nigdy nie używał ;-) Bardzo łatwo popsuć dane. Nawet gdy masz świetną obsługę i nieznane enumy są zmieniane w UNKNOWN... to problem jest w tym, że jeśli te dane zapiszesz i przekażesz dalej to kolejny klient dostał właśnie dane z usuniętą informacją. Ty chcesz zmienić pole innego typu, ale przy okazji popsujesz pole którego nawet nie używasz....
Tak, to można też obejść, ale to już jest dobudowywanie na siłę maszyny która będzie naprawiać nasz zły design.
Tak, to można też obejść, ale to już jest dobudowywanie na siłę maszyny która będzie naprawiać nasz zły design.
Myślę, że enum jest podobny trochę do developmentu z branchami. Daje złudne poczucie bezpieczeństwa.
Wielu developerów tłumaczy fakt używania branchy tym, że czują się bezpieczni bo nie mają konfliktów... do momentu integracji, która jest potencjalnie największym źródlem problemów... Enum też daje poczucie bezpieczeństwa i elegancji. Wszystko ładnie się konwertuje, nie używamy Stringów, nie ryzykujemy porównywania == między typami, których nie wolno tak porównywać.... tylko, że to też jest takie pozorne bezpieczeństwo, bo w momencie gdy schema się zmienia to nagle staje się widoczna w wielu miejscach, których byśmy mogli nawet nie podejrzewać o to, że będą dotknięte przez tę zmianę.....
Tu pamiętam, że robiąc za orkiestrator do deploymentów widzieliśmy pole status i pokazywaliśmy je userowi wg zasady, że mieliśmy:
var textToShow = translations.getOrDefault(s, s);
i jakież było nasze zaskoczenie jak przebudowa w drugim systemie, który zaczął wysyłać w pewnym momencie nieznane stany to wszystko nadal działało? ;-)
Tajemnica była w tym, że wymusiliśmy kontrakt "fireAndForget", czyli my dawaliśmy pakiet do deploymentu, ale maszyna stanów była w kodzie, który robił deployment (w operatorze dla K8S), więc on sobie wprowadził swoje enumy i rozszerzył ich listę.... ale nas nie popsuł ;-)
Podobne postybeta
Lubię enumy
Javozagadka ;-)
finalize() - do czego służy, a do czego nie i z czym to się je.
Nie lenistwo, a strach. Prawdziwe źródło długu technicznego
Modale nie takie dobre dla Androida ;-)
Brak komentarzy:
Prześlij komentarz