Przemyślenia zainspirowane https://4programmers.net/Forum/Oceny_i_recenzje/359166-kalkulator_wersja_10
Jak to się często zdarza, w tym wątku widzimy duży nacisk na oddzielenie UI od logiki.
Po co się to robi? O ile się nie mylę, cel rygorystycznego oddzielania UI od logiki jest taki, by móc budować wiele interfejsów do tej samej logiki.
Czy to jednak często nie podpada pod YAGNI? Mamy dobę aplikacji webowych. Nie będzie raczej tak, że zbudujemy jeden interfejs Windowsowy, drugi Macowy, trzeci Linuxowy, czwarty Androidowy, piąty iOSowy, szósty konsolowy i jeszcze na dokładkę siódmy webowy. Nie, w praktyce najczęściej będzie tylko jeden interfejs: webowy. A już niemal na pewno podpada pod to apka taka, jak kalkulator. A jeśli już konieczne będziemy chcieli robić interfejs desktopowy / mobilny, to raczej użyjemy Electrona.
Zgadzam się, będą aplikacje, w których wiele interfejsów będzie jak najbardziej potrzebne. Ale w apkach, w których niemal na pewno będzie zawsze tylko jeden interfejs, czemu nie można wrzucić logiki do np. kontrolerów Reacta?
Ale załóżmy teraz, że jednak chcemy mieć wiele interfejsów. Może wystawiamy RESTowe API do naszego kalkulatora. Może nawet wystawiamy interfejs konsolowy.
Jaki ma więc sens zasada:
TomRiddle napisał(a):
Od razu uprzedzam pytanie; większość nowicjuszy jak zobaczy taki kod to się złapie za głowie i powie, matko co to jest, po co robisz
calc.type("1"); calc.type("7"); calc.type("3");
, skoro mógłbyścalc.type("173")
? Owszem, móglbyś, grybyś projektował interfejs programistyczny klasy (funkcje, metody, klasy). Ale Ty projektujesz interfejs użytkownika, a użytkonik będzie klikał liczby przyciskami, zawsze jedną na raz, więć musisz to wziąć pod uwagę.
Oczywiście: interfejs musi być pozbawiony logiki. Łączenie wielu naciśnięć klawiszy w jeden string to jest logika. Musi więc to wylecieć z UI.
Czy to jednak, paradoksalnie, nie wiąże nam logiki z interfejsem? Abstrakcja wycieka, bowiem logika wystawia właśnie takie metody, które mają przełożenie 1-1 z kliknięciami użytkownika. Zatem poważniejsza niż czysto kosmetyczna zmiana interfejsu lub dodanie nowego, wyraźnie różnego interfejsu będzie wymagało zmian w logice. Mamy więc tight coupling między interfesjem a logiką, a chodziło nam o coś dokładnie przeciwnego.
REST Api lub konsola nie będą operowały na uderzeniach klawiszy, tylko na całych napisach.
Moje intuicyjne podejście byłoby odmienne. Zakładając, że będziemy potrzebować rozmaitych interfejsów:
- Budujemy logikę biznesową tak, by naprawdę nie musiała ona absolutnie nic zakładać o interfejsie (np. by nie wiedziała, że cyfry wprowadza się przez uderzenia klawiszy) - czyli wystawiamy rzeczywiście
calc.type("173")
, a może nawet coś w rodzajucalc.mult(calc.parenth(calc.add("173", "432")), "234")
- Budujemy interfejsy tak, by były one w stanie przetłumaczyć input w formacie dla nich naturalnym (uderzenia klawiszy w przypadku apki webowej, JSONy w przypadku API Restowego, napisy i przełączniki w przypadku konsoli, krzywe w jakimś dziwnym przypadku kalkulatora ewaluującego odręczne napisy na tablecie graficznym, ..., ...) na ten wspólny format wystawiony przez logikę biznesową dla interfejsów. Dlatego interfejsy będą jednak zawierały logikę potrzebną na dokonanie takiego przetłumaczenia. Np. do interfejsu, a nie logiki będzie należało połączenie "1", "7" i "3" w "173"
Zdaję sobie sprawę, że to, co napisałem, sprawi, że doświadczeni programiści złapią się za głowę. OK, czy mógłby mi więc ktoś wytłumaczyć, gdzie popełniam błąd?