Sens i sposób oddzielania logiki od interfejsu

0

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 rodzaju calc.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?

2

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.

Idąc tym tokiem rozumowania, każdy „if” to logika, wiec powinien wylecieć z UI do backendu. Mieszasz logikę obsługi interfejsu z logika biznesowa - 2 różne tematy.

Odnośnie wielu interfejsów - naturalnie masz 2: UI i testy :) Chcesz mieć możliwość testowania logiki aplikacji bez guja.

Co daje rozdzielenie logiki od prezentacji - zobacz co się dzieje, gdy np. w obsłudze przycisku masz zaszyty kawałek logiki biznesowej. Taki kod jest niereuzywalny, logika aplikacji jest rozproszona w różnych miejscach - cierpi utrzymywalność i testowalnosc. Nie mówiąc już o tym, że masz monolit i nie możesz rozwijać tych warstw niezależnie.

Do reszty chętnie odniosę się później, gdyż obecnie przebywam na wakacjach 😎

2
YetAnohterone napisał(a):

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.

oddzielenie logiki od interfejsu ułatwia testowanie. im więcej logiki wymaga odpalenia interfejsu podczas testowania (obojętnie czy headless czy nie), tym trudniej się taki system testuje.

z drugiej strony, jeśli skutkiem rygorystycznego oddzielania UI od logiki będzie znaczne rozdmuchanie kodu to to też jest złe, bo im więcej kodu tym więcej miejsc, gdzie mogą czaić się błędy. trzeba znaleźć jakiś złoty środek (w zależności od sytuacji).

0

Zależy to od tego, co nazwiemy kalkulatorem. W takich ćwiczebnych projektach zwykle nie chodzi o względy praktyczne, tylko o napisanie aplikacji, która emuluje kalkulator prosty (a nie np. naukowy, czy choćby taki, który pozwala na złożone wyrażenia). Stąd zakładamy, że cyfry wprowadza się przez uderzenia klawiszy, bo taki przyjmujemy sobie model działania kalkulatora. Cała idea takich aplikacji polega na tym, żeby napisać emulator kalkulatora.

Jeśli jednak celem jest udostępnienie użytkownikowi sposobu na liczenie czegoś, to faktycznie lepiej się nie ograniczać do konkretnego sposobu wprowadzania tych liczb. Bo może na początku wymyślimy kalkulator z przyciskami, potem udostępnimy możliwość wprowadzania wyrażeń typu 2 + 2 * 2, a później stwierdzimy, że najlepiej zrobić tak, żeby użytkownik przeciągał jakieś elementy wizualne (wyobraźmy sobie aplikację, gdzie ten "kalkulator" ma być tylko widżetem, który pomaga użytkownikowi przeliczyć np. cenę produktu).

Tylko, że mam wrażenie, że twój post jest o dwóch różnych rzeczach.

  • jak się powinno robić kalkulator
  • czy podział na logikę i interfejs ma sens

1 użytkowników online, w tym zalogowanych: 0, gości: 1