Jak precyzyjnie zdefiniować pojedynczą odpowiedzialność w kontekście zasady SRP z S.O.L.I.D

0

Zastanawia mnie jak wykryć czy dana klasa, lub funkcja, metoda ma pojedynczą odpowiedzialność. Poczytałem nieco na temat tej zasady i nie wydaje mi się to jasne. Weźmy przykład, taki artykuł : https://levelup.gitconnected.com/single-responsibility-principle-a-beginners-note-cb1eaba1fecd

Jako poprawną klasę nie łamiącą SRP autor podaje taki przykład :

class Accounting extends Employee {
  calculatePay (): number {
    // implement own algorithm
  }
  reportHours (): number {
    // implement own algorithm
  }
}

No i autor podaje też takie wyjaśnienie

You can implement SRP in your problems by asking a simple question before you make any changes: “What is the responsibility of your class/component/microservice?”

If your answer includes the word “and”, you’re most likely breaking the single responsibility principle. Then, it’s better to take a step back and rethink your current approach. There is most likely a better way to implement it.

No to zgodnie z tym cytatem klasa podana powyżej łamie tą zasadę bo można odpowiedzieć, że ma 2 powody do zmiany : zmiana algorytmu liczącego i zmiana algorytmu raportującego. To jak w końcu? Jak dokładnie wykryć, zdefiniować pojedynczą odpowiedzialność? Powyższe wyjaśnienie zdaje się nie zdawać egzaminu.

6

Jak dokładnie wykryć, zdefiniować pojedynczą odpowiedzialność?

Gdyby tak się dało, to prawdopodobnie nie istniałyby tysiące różnych artykułów na ten temat :-)

Ja sam lubię definicję:

Nie powinno być więcej niż jednego powodu do zmiany klasy.

... która - choć nadal pozostaje trochę mglista - to przynajmniej łączy się z tym, co pisze @twoj_stary_pijany: klasa/moduł itd. powinna robić jedną rzecz "wysokopoziomową", na którą może się składać wiele rzeczy "niskopoziomowych".

I tak na przykład class Accounting IMO jeszcze pasuje do tej definicji (zwłaszcza w kontekście przytoczonym w artykule), ponieważ grupuje ona akcje niskopoziomowe w jeden zbiór akcji wysokopoziomowych, a typowy SRP-puryzm możesz otrzymać robiąc dodatkowo:

class Accounting extends Employee {
  calculatePay (): number {
    return AccountingPayCalculator(...)->calculate();
  }
  
  reportHours (): number {
    return AccountingHourReporter(...)->report();
  }
}

Co wygląda trochę jak masło maślane (gdzie znawcy nazwaliby to fasadą), lecz przynajmniej trzyma się zasady, że nie powinno być więcej niż jednego powodu do zmiany tej klasy:

  • jeśli będzie potrzeba zmiany algorytmu obliczania wypłaty, zmieni się AccountingPayCalculator,
  • jeśli będzie potrzeba zmiany algorytmy raportowania godzin, zmieni się AccountingHourReporter,
  • (wreszcie) jeśli będzie potrzeba usunięcia / dorzucenia jakiejś wysokopoziomowej funkcjonalności związanej z - uhm - accountingiem, to zmieni się Accounting.

Jaka zatem zasada definiuje czy łamiemy SRP czy też nie? Jak to sprawdzić?

class Microphone {
  record(): void;
  saveToFile(): void;
  loadFromFile(): Recording;
}

... to prawdopodobnie za dużo funkcjonalności, a...

class MicrophoneRecorderForEvenWeekdays {}
class MicrophoneRecorderForOddWeekdays {}
class MicrophoneFileSaver {}
class MicrophoneFileLoaderMp3Local {}
class MicrophoneFileLoaderMp3OverNetwork {}
class MicrophoneFileLoaderMp3OverNetworkIfSlowSpeed {}

... to prawdopodobnie za dużo klas.

Interpoluj, improwizuj, adaptuj, owerkomuj

Żarty na bok - nie ma jednoznacznej metryki SRP (oraz większości innych reguł, FWIW) - choć nie jest to najlepszą poradą, to z czasem po prostu zaczniesz zauważać, że na coś powinno składać się więcej / mniej abstrakcji :-) (a w razie wątpliwości, buduj mniej abstrakcji!)

Tym niemniej, srp = max 1 powód do zmiany klasy/modułu to imo całkiem dobra rule of thumb.

4

Jak czytam takie rozkminy, to mam wrażenie, że sztywne trzymanie się tego co jest w SOLID, jest większym problemem niż brak świadomości tych zasad. Martin napisał o "jednym powodzie do zmiany", ale nie napisał (albo nie doczytałem, bo zasnąłem), jak szeroki ten powód może być. Jeżeli napiszę cały system w pojedynczej klasie, to nadal mogę napisać, że mam pojedynczy powód do zmiany, bo przecież moge sobie sam ze sobą ustalić, że dowolna zmiana zachowania aplikacji jest takim atomicznym powodem. Idąc w drugą stronę, każda metoda (coś robiąca), a nawet kawałek tej metody powinien lecieć do osobnej klasy, bo przecież nikt ci nie zabronić pociąć wszystkiego na kawałki.

W przykładzie, który podajesz masz 2 kompletnie niezależne kawałki logiki. Właściwie już samo Accounting extends Employee jest herezją, bo w jaki niby sposób księgowość rozszerza pracownika? Co jeżeli będziemy chcieli w tej księgowości wystawić jakąś fakturę?

Co się stanie, jeżeli w systemie pojawi się więcej niż jeden sposób wyliczania pensji, albo więcej niż jeden sposób raportowania czasu pracy? Jeżeli będziesz miał po 3 takie algorytmy, to będziesz musiał zaimplementować 9 rozszerzeń klasy z przykładu i zmagać się z dylematem po której gałęzi dziedziczyć w pierwszej kolejności, a można to zrobić np. tak:

public class PayrollFacade{
  private final TimeReporting timeReporting;
  private final WageCalculator wageCalculator;
  private final Employee employee;
  
  PayrollFacade(TimeReporting timeReporting, WageCalculator wageCalculator, Employee employee):{......}

  public final int calculatePay(){
    return wageCalculator.calculatePay(employee);
  }

  ....
}

I masz separację danych od logiki, wyciągnięcie niezależnych algorytmów do niezależnych klas, fasadę, która nie ma żadnej wiedzy o tym jak coś zrobić. Kiedy dzielić kod na klasy? Jeżeli masz 2 kawałki logiki, które nie musza o sobie wiedzieć, to często jest to całkiem dobry powód, żeby trzymać je osobno.

Bardzo pobieżnie pisząc:
Ciężko podzielić kod za bardzo. Jak możesz gdzieś wyznaczyć jakiś kawałek o którym jesteś w stanie powiedzieć jaką ma odpowiedzialność, co ma przyjmować i co z siebie wyrzucać, to warto go wydzielić do osobnej klasy, lub funkcji.
Kod znacznie prościej się scala niż dzieli, więc lepiej przegiąć z siekaniem, niż wrzucić wszystko w jedną klasę.
Dziedziczenie bardzo rzadko jest dobrym pomysłem

3

Na SRP patrzyłem przez długi czas tylko z perspektywy kodu, ale po różnych lekturach, zmieniłem rozumienie i teraz patrzę tak:

Pojawiają się aktorzy biznesowi ze swoimi wymaganiami oraz przypadki użycia systemu, w ramach których wymagania są realizowane. Przypadki użycia przekładają się na konwersację z systemem (GUI i wypełnianie formatek, czy wywołania API) i na samym końcu jest kod, w którym zasada SRP może być naruszona.

Jeśli ten sam kod (klasa) zmienia się ze względu na zmianę wymagań pochodzących od więcej niż jednego aktora biznesowego, to SRP zostaje naruszone.

W przytoczonym przykładzie: Accounting (przekłada się na aktora z działu księgowości?) wydaje mi się nie naruszać tej zasady (calculatePay i reportHours wyglądają na powiązane z księgowaniem). Gdyby wymagania innych aktorów (SecurityOfficer: Employee, czy LegalOfficer : Employee.), wymuszały zmianę w implementacji Accounting, wówczas SRP zostałaby naruszona.
W efekcie, zmiana implementacji, wynikająca ze zmiany wymagań jednego z aktorów biznesowych, mogłaby rzutować na funkcjonalność wykorzystywaną przez innych aktorów.

1

@yarel: Nie zgodzę się, jak pojedynczy aktor biznesowy wpadnie na pomysł kompletnej zmiany działania systemu, to też będzie to "pojedynczy powód do zmiany"? Czy np. kalkulator mający jednego aktora biznesowego zawsze będzie spełniał SRP? Nawet jak walniemy całą logikę w ramach jednej formatki UI'owej?

2

Ciężko mi się nie zgodzić z @Patryk27 , jak masz w językach programowania moduł Math, to raczej nie masz na siłę wydzielania tej klasy do MathRounding, MathTrigonometrics itp, raczej masz 1 klasę / moduł / serwis / plik grupujący kontekstowo operacje.

Co do samego SRP mi najbardziej pomaga w dzieleniu takiego kodu zasady z unixa
https://en.wikipedia.org/wiki/Unix_philosophy
https://homepage.cs.uri.edu/~thenry/resources/unix_art/ch01s06.html

A głównie - (i) Make each program do one thing well. potem patrzę na takiego grep'a, find'a, ls'a... - robią bardzo dobrze to, do czego zostały zaprojektowane i ciężko tam dodać cokolwiek więcej. Bardziej patrzę z perspektywy użyteczności / sposobu użycia tego kawałka kodu i czy warto cokolwiek wydzielić z niego.

3

Dla mnie zbiór modułów/klasy/funkcji jest bardziej SRP jeśli da się go używać na większą liczbę sposobów. Przykładowo unixowe polecenia i pipy są bardziej SRP niż find w IDE bo polecenia są bardziej specyficzne i mogę je układać w dowolny sposób. Opcja Find w IDE pozwala na wyszczególnienie tylko tego co chciał autor w GUI

2

Przykłady są niezbyt trafne.

Pomijam już uproszczenia (np. w realnej sytuacji tytuł książki byłby ustawiany przez konstruktor, a nie wpisany na sztywno), to czy klasa Book faktycznie powinna mieć metody turnPage i getCurrentPage? Niekoniecznie.

Powiedzmy, że tworzymy przeglądarkę e-booków.

Wtedy lepiej byłoby to zrobić tak, że klasa Book reprezentowałaby konkretny tytuł (np. "Romeo i Julia") i mogłaby zawierać takie elementy identyfikujące daną książkę jak tytuł, autor, wydawnictwo, rok wydania, nazwisko tłumacza, ISBN, liczba stron itp.

Natomiast to, na której stronie jest otwarty dany e-book, wydaje się w ogóle nie mieć wiele wspólnego z książką, tylko z samą przeglądarką. Prędzej zrobiłbym klasę BookView czy BookSession, która reprezentowałaby sesję z daną książką/stan przeglądarki (z przypisaną aktualną stroną, zakładkami, notatkami użytkownika itp.).

Bo co ma aktualna czytana strona do samego tytułu książki i autora? Strona może się zmienić, a autor pozostaje ten sam.

1

Może warto spojrzeć na całe to SRP od strony "po co?", zamiast szukać idealnej definicji? Przecież SRP nie jest celem samo w sobie. SRP (pod różnymi nazwami) występuje na każdym z poziomów programowania:

  • niepodzielne pole w bazie danych (NF1)

  • ograniczona funkcjonalnie metoda (np. CQS)

  • ograniczona odpowiedzialność klasy (SRP)

  • modułu (nie kojarzę żadnego "principle" ale przecież właśnie dlatego się robi moduły)

  • usługi (np. CQRS)

  • produktu w portfolio (też warto wiedzieć, który z systemów jest "właścicielem" jakiegoś kawałka)

    Najważniejszy w moim przekonaniu powód, kryje się przytoczonej tu definicji "pojedynczy powód do zmiany", a chcemy takiego stanu, żeby ograniczyć w przyszłości zakres tych zmian. Jeżeli kod działa, to bez wprowadzania zmian będzie działał dalej, jak wprowadzimy, to może przestać/ Im mniejszego fragmentu będzie dotyczyć ta zmiana tym łatwiej nad nią zapanować. Rozpatrywanie SOLID posiekanego na fragmenty kryjące się pod tymi literkami nie ma sensu, bo co nam po kodzie, który ma niby drobnoziarniste odpowiedzialności klas, jak same klasy są ze sobą uturlane w jedną wielką kuleczkę nawozu toczoną przez pokolenia programistów? Jak ktoś się skoncentrował na S, a OLID miał kompletnie w d... to co z tego, że klasy mają pojedyncze odpowiedzialności, jak i tak strach coś dotknąć, bo powiązania zostały przeniesione wyżej. Użyć tych klas ponownie (drugi powód SRP) tez się nie da, bo działają tylko w jakiejś konkretnej, nieznanej nikomu konfiguracji, rozszerzyć też się nie da bo gdzieś tam ktoś wpisał if(shape typeof Triangle) i kwadratu to nie obsłuży, bo nie ma odpowiedniego if'a. No i przetestować też się nie da (nawet jeżeli byłoby warto).

Ktoś tu się zastanawiał, czy pakiet Math spełnia wymagania SRP. Oczywiście, że spełnia, mamy od groma funkcji pure w pojedynczym pliku, który został nazwany klasą wyłącznie dlatego, że ktoś kiedyś bardzo upierał się przy nazywaniu Javy językiem obiektowym i wszystko musi być kawałkiem klasy, nawet funkcje pure, nie mające z tą klasą nic wspólnego.

0
piotrpo napisał(a):

@yarel: Nie zgodzę się, jak pojedynczy aktor biznesowy wpadnie na pomysł kompletnej zmiany działania systemu, to też będzie to "pojedynczy powód do zmiany"?

Mogę się mylić w tym rozumieniu SRP i chętnie skoryguję spojrzenia, ale rozumiem to tak, że ilość zmienianych przypadków użycia nie ma nic do rzeczy, tylko to czy mają współdzielony kod.

"Kompletna zmiana działania" to bardzo ogólne stwierdzenie i wypada zapytać, które przypadki użycia się zmienią i czy są one współdzielone przez różnych aktorów?

Być może zmienią się wszystkie przypadki użycia, ale każdy z 1 powodu (aktora biznesowego/interesariusza). To nie jest tak, że np. Księgowość będzie chciała zmieniać przypadki użycia dla departamentu prawnego.

Jeśli implementujesz 2 przypadki użycia z różnych grup (Księgowość, Marketing) i one współdzielą kod, to będą dwa powody do zmiany i złamanie SRP. Jeśli nie dzielą kodu, to nawet jak zmienisz
...dziesiąt implementacji przypadków użycia, to masz nadal 1 powód do zmiany kodu per przypadek.

Czy np. kalkulator mający jednego aktora biznesowego zawsze będzie spełniał SRP? Nawet jak walniemy całą logikę w ramach jednej formatki UI'owej?

Nie wiem czy to dobry przykład:

a) calc.exe -> Jako użytkownik końcowy kalkulatora nie definiuję funkcjonalności i nie mam powodu/wpływu na zmianę

b) Ile taki kalkulator ma przypadków użycia? Ja widzę jeden (więc nie ma co współdzielić z innymi przypadkami użycia)

UC#1 - Oblicz wartość funkcji, który to można rozbić na kroki:

Krok1: Wprowadź wyrażenie
Krok2: Oblicz wartość wyrażenia

Dlaczego chciałbyś zmieniać kod kalkulatora? Jaka to byłaby zmiana?

5
Mostek87 napisał(a):

Zastanawia mnie jak wykryć czy dana klasa, lub funkcja, metoda ma pojedynczą odpowiedzialność.

W ten sam sposób jak większość kwestii w inżynierii - na podstawie doświadczenia, intuicji i zdrowego rozsądku.

No to zgodnie z tym cytatem klasa podana powyżej łamie tą zasadę bo można odpowiedzieć, że ma 2 powody do zmiany : zmiana algorytmu liczącego i zmiana algorytmu raportującego. To jak w końcu?

Autor po pierwsze sam nie rozumie, co tłumaczy, po drugie dobrał absurdalny przykład - każdy przykład, w którym zwierzątka albo ludzie dziedziczą z siebie jest pozbawiony sensu.

Jak dokładnie wykryć, zdefiniować pojedynczą odpowiedzialność? Powyższe wyjaśnienie zdaje się nie zdawać egzaminu.

Jakby się dało dokładnie, to by to było twierdzenie, a nie zasada.

1

Cóż, podsumowując wychodzi na to, że nie udało się wypracować jasnej definicji, trzeba po prostu na przykładach praktycznych tego doświadczyć.

7

Powiem od siebie tyle, że dobre zasady, SOLID, DRY, KISS, etc. one nie są celami samymi w sobie. Celem jest stworzenie utrzymywalnego i wysokiej jakości, i kiedy się go takiego stworzy - wtedy zaczyna się zauważać że "przypadkiem" kod zaczyna spełniać te zasady. Z reguły dobry kod spełnia te zasady; ale one nie są celem samym w sobie i po prostu "wrzucenie" ich na siłę nie koniecznie musi znaczyć o jakości kodu. Dla przykładu - z reguły jak ktoś się umyje, to nie śmierdzi. Ale to nie znaczy że każdy kto nie śmierdzi się myje. Przyczynowość nie idzie w stornę "stosuj SRP" -> "to będziesz miał dobry kod".

Co do samego SRP - Ogólnie, nazwa w SRP jest trochę problemem. "Single responsibility" jest trochę nieosiągalne. Nie tylko w programowaniu, ale w ogóle w życiu. Wyobraź sobie że ktoś daje Ci zasadę "zjedz tylko jeden posiłek dziennie". I co, nie możesz np zjeść kanapki a potem kiełby; ale możesz to połączyć w hotdoga, i to już jedna rzecz. Po drugie, jeśli miałbyś klasę która na prawdę ma jedną odpowiedzialność, to mógłbyś też wydzielić drugą z niej która również ma jedną - bo co jeśli masz klasę która ma jedną odpowiedzialność i podzielisz ją na dwie? Wychodzą Ci dwie które mają pół odpowiedzialności? Czy może każda ma po jednej? Ale jeśli mają po jednej, to jak je połączysz to mają dwie? Czy nadal jedną? chyba że mają jedną, ale wspólną? Ale skoro mają wspólną to czemu dało się je rozdzielić. Podobnie gdyby ktoś Ci powiedział: "Napisz tylko jedną opowieść", ale co jest tą "jedną opowieścią"? Książka? Może rozdział, albo saga?

Więc z uwagi na to że programy są kompozytami funkcji i klas - idea "pojedynczej odpowiedzialności" nie ma prawa bytu (chyba że jako uproszczenie).

Nie da się stwierdzić czy klasa ma jedną odpowiedzialność czy więcej, bo to zależy od tego co uznasz za odpowiedzialność a co nie. Można by napisać funkcję na 10000 linijek która konwertuje jpg na png i powiedzieć "Ta funkcja ma jedną odpowiedzialność - i jest nią konwertowanie jpg na png".

Tak na prawdę w SRP nie chodzi o żadną "pojedynczą odpowiedzialność", "robienie jednej rzeczy", "powody do zmiany", to są brednie - albo ewentualnie określenia które mogą pomóc amatorom. W SRP tak na prawdę chodzi o ograniczenie scope'u i poziomów abstrakcji jednostki (klasy, moduł lub funkcji). Im mniejszy scope, i mniej poziomów abstrakcji, tym bardziej prawdopodobne, że kodzik nam spełnia solid.

Ja osobiście uważam że kod spełnia SRP, tak jak sugeruje Robert Martin w swoich filmach, czyli kod wtedy spełnia SRP, kiedy nie można w sensowny sposób wydzielić metod z kodu. Jeśli możesz, to powinieneś.


Tak może tytułem luźnej dygresji, powiem tak - jeśli chcesz być dobrym programistą, i chcesz się nauczyć co to są właśnie SRP, SOLID, MVC, TDD, MVVM, i inne takie skrótowce albo elementy - to zapomnij o prostych nazwach i sposobach. Podobnie z paradygmatami. Programowanie to nie jest prosta sprawa, i takich skomplikowanych konceptów jak SRP albo MVC nie da się streścić jednym czy kilkoma zdaniami. Jeśli Ci się zdaje, że coś jest proste, np nie wiem "TDD" to "pisanie testów przed kodem", a "LSP" to "możliwość używania dziecka tam gdzie rodzica", to szybko się przekonasz że to nie prawda, i tak na prawdę te koncepty odnoszą się do dużo bardziej skomplikowanych idei.

1

Programowałem sobie właśnie aplikację, i napisałem całkiem zgrabną małą klasę, w mojej opinii spełnia SRP. Ale ma taką dziwną cechę, którą pomyślałem że się podzielę.

Otóż to jest klasa do mojej biblioteki T-Regx, i potrzebowałem kodu który weźmie wzorzec /\w+\n/, znajdzie w nim konwencję nowych linii, oraz zwróci możliwe numery bajtów odpowiednich znaków. Kod tej klasy to mniej więcej 45 linijek, 4 metody + konstruktor. Klasa moim zdaniem spełnia SRP, ale jakbym miał komuś opowiedzieć, co ona robi to musiałbym powiedzieć:

"Ta klasa parsuje ciąg oraz znajduje znaki nowych linii oraz zwraca odpowiednie bajty".

Muszę użyć słowa "oraz", żeby opisać komuś co ta klasa robi, żeby ją zrozumiał; ale to nie znaczy w żaden sposób żebybym musiał dzielić tą klasę na trzy, nie miałoby to żądnego sensu. Kod wymaga zamiany stringa pattern, na odpowiedni array[byte]. Rozdzielane tego nie tylko nie miałoby sensu, ale zmniejszyło by cohesion klasy i stałaby się mniej czytelna i niepotrzebnie większą. To kolejny argument za tym żę zasada jeśli móżesz powiedzeć "klasa robi A ORAZ B" to znaczy że nie spełniasz SRP, to nie prawda.

6
somekind napisał(a):

Jak dokładnie wykryć, zdefiniować pojedynczą odpowiedzialność? Powyższe wyjaśnienie zdaje się nie zdawać egzaminu.

Jakby się dało dokładnie, to by to było twierdzenie, a nie zasada.

Twierdzenie to by było, jakby było matematyczną prawdą.
SRP to zasada, która jest kiepska (zła), ponieważ nie ma dobrej i jednoznacznej definicji Single Responsibility.

Co więcej, w praktyce do dużej części sensownego kodu można się przyczepić, że łamie SRP (chyba nawet to ćwiczyliśmy na tym forum).
Jest trochę prób naprawienia SRP poprzez wprowadzenie alternatywnej, sensownej definicji, ale nic się z tego nie przebiło (zbyt skomplikowane, albo nadal mętne).

2

@jarekr000000: To co piszesz dotyczy wszystkich zasad z SOLID, jeżeli wejdą komuś zbyt mocno. Wtedy ktoś się przyczepi, że Math łamie SRP i zamiast Math.sin(Math.PI) skończy się na pisaniu new Sin().calculate(new Pi().asDouble() a później będziemy mieli dyskusję, czy aby słuszne jest, że \pi jest final, bo niby jest close na modyfikacje, ale ni cholery open na rozszerzanie. O jawnym łamaniu LSP przez klasy sealed nawet nie wspominam.

0

Wtedy ktoś się przyczepi, że Math łamie SRP i zamiast Math.sin(Math.PI) skończy się na pisaniu new Sin().calculate(new Pi().asDouble() a później będziemy mieli dyskusję, czy aby słuszne jest, że \pi jest final, bo niby jest close na modyfikacje, ale ni cholery open na rozszerzanie

To znaczy tylko że nie rozumie open closed principle.

4
jarekr000000 napisał(a):

Twierdzenie to by było, jakby było matematyczną prawdą.

To się chyba rozumie samo przez się. Są jakieś niedokładne matematyczne prawdy?

Jeśli możesz coś określić dokładnie, to znaczy, że jest to matematyka i można ułożyć twierdzenie.
Jeśli się nie da dokładnie, to są co najwyżej prawa lub zasady, i jest to jakaś inna dziedzina nauki.

SRP to zasada, która jest kiepska (zła), ponieważ nie ma dobrej i jednoznacznej definicji Single Responsibility.

Nie wiem, czy zasada jest zła, czy złe jest po prostu nadinterpretowanie. Nie wszystko da się zdefiniować, ale zamiast poszukiwać dokładnej definicji lepiej postarać się zrozumieć ideę.

3
somekind napisał(a):

Nie wiem, czy zasada jest zła, czy złe jest po prostu nadinterpretowanie. Nie wszystko da się zdefiniować, ale zamiast poszukiwać dokładnej definicji lepiej postarać się zrozumieć ideę.

Tak, to możemy sobie robić w ekonomii, teologii czy innych pseudonaukach.
W IT nie mamy nawet papieża, który w razie czego wydałby interpretację - jeśli ktoś słabiej zrozumie ideę.
Mętne definicje prowadzą do jałowych dyskusji i powodują, że zasady nie działają dobrze.
Jak mam w kodzie zasadę, że metoda nie może być dłuższa niż 20 linii,to germański architekt wrypując coś na 35 nie może się kłócić, że przestrzega zasad.
Ale jak wrzuci na 19 linijek i pomiesza ileś warstw to nadal może uzasadniać, że w jego rozumieniu kod spełnia SRP. I mamy syf.

4
jarekr000000 napisał(a):

Tak, to możemy sobie robić w ekonomii, teologii czy innych pseudonaukach.

Ekonomia nie jest "pseudonauką". Podobnie jak nauki przyrodnicze próbuje na podstawie obserwacji rzeczywistości tworzyć ogólne zasady. W naukach przyrodniczych raczej ciężko szukać jakichś uniwersalnych prawd, bo nawet zasada zachowania energii może potencjalnie okazać się czymś co nie działa w warunkach o których aktualnie nie mamy pojęcia. Nie dołączyłem do płaskoziemców, tylko zwracam uwagę, że nauki przyrodnicze bazują na obserwacjach i eksperymentach. W matematyce bazujemy na aksjomatach, które też często mają mętny opis - np. nie masz czegoś takiego jak "definicja punktu", ale jakoś nikomu to specjalnie nie przeszkadza i (chyba) nikt nie toczy dyskusji jak nieskończenie wiele punktów nie mających wymiarów może utworzyć coś, to wymiary ma.

SOLID nie ma nic wspólnego z nauką, to jedynie zestaw wskazówek jak wykonywać jakąś pracę. Coś na poziomie "śrubę najpierw dokręcaj ręką, a nie kluczem" w mechanice samochodowej. To nie jest jakaś uniwersalna prawda, tylko praktyczna wskazówka, żeby gwintu nie zrąbać. Jeżeli klucz dynamometryczny ustawiony na 0.1Nm to stanie się coś złego, jeżeli go użyjesz zamiast ręki? Da się tę zasadę zastosować jak śruba waży 100kg?

W IT nie mamy nawet papieża, który w razie czego wydałby interpretację - jeśli ktoś słabiej zrozumie ideę.
Mętne definicje prowadzą do jałowych dyskusji i powodują, że zasady nie działają dobrze.

Ścisłe definicje prowadzą do fanatyzmu "daily musi trwać 15 minut", nie ważne, że akurat trzeba coś technicznego przegadać, albo 3 gości nie ma sobie wiele do powiedzenia i się wyrobili w 5 minut.

Jak mam w kodzie zasadę, że metoda nie może być dłuższa niż 20 linii,to germański architekt wrypując coś na 35 nie może się kłócić, że przestrzega zasad.

A ta zasada zawsze jest słuszna? Dlaczego 20 linii, a nie np. 19, albo 21? Jak będę miał wykonać np. na jakiejś bitmapie 25 przekształceń w określonej kolejności to koniecznie muszę ten kod dzielić na pierwsze20Linii() i drugie20Linii()? Czy może złożyć je wszystkie w jakąś macierz przekształceń i wtedy już nikt nie skuma co się dzieje, ale kod będzie miał 3 linijki?

4
piotrpo napisał(a):

Nauki przyrodnicze bazują na precyzyjnych matematycznych modelach/definicjach. Te modele mogą się nie zgadzać z eksperymentami i zostac odrzucane, ale muszą być precyzyjnie zdefiniowane.

Inżynieria oprogramowania bazuje na "computer science" - co prawda nie widzę też potrzeby, żeby wszystko było precyzyjnie uregulowane, ale kod można dość jasno opisac różnymi metrykami, dzięki czemu można by operować na precyzyjnych zasadach.

Zasada 20 linii wcale nie musi być słuszna (efektywna), to zupełnie nieistotne. Ale jeśli w danym projekcie przyjeliśmy taką zasadę, to możemy dość jasno określić, który kod ją łamie, a który nie. W odróżnieniu od SRP, gdzie kwestia łamania jest zależna od wiary oceniającego.

1

@jarekr000000: Ale skąd potrzeba posiadania takich definicji? To, że istnieje coś takiego jak "computer science", nie oznacza z automatu, że SOLID jest częścią tej nauki, chociaż pewnie kilka setek doktoratów na ten temat napisano. Jak masz problem z gościem, który w widoku korzysta z warstwy danych, to wprowadź w projekcie podział na warstwy, czy obszary i jasne zasady czy warstwa X może mieć jakąkolwiek wiedzę o warstwie Z. Ja wolę nie mieć reguł, które każą mi np. zawsze rozdzielać odpowiedzialność zapisu i odczytu.

Gdyby dalo się napisać precyzyjny ciąg zasad jak pisać kod, to niepotrzebni byliby do tego ludzie. Komputery bardzo dobrze radzą sobie z przestrzeganiem zasad i wykonywaniem ustalonych sekwencji czynności.

Precyzyjne metryki dotyczące kodu służą głównie różnej maści managerom i innym SQA, jedyne co z nich wynika, to puste rozmowy na tematy typu "oj nie dobrze, w naszej organizacji wymagamy 90% pokrycia testami jednostkowymi a wy macie 87.5%, przygotujcie plan naprawczy".

1
piotrpo napisał(a):

@jarekr000000: Ale skąd potrzeba posiadania takich definicji?

Ale czemu pytasz się Jarka po co mu ta definicja? To bardziej Opa powinieneś się pytać po comu to. Wszak to on założył ten wątek.

A po co np mi mogłaby być potrzebna ta definicja? Taki kejs sobie wyobrażam:

  1. Oddaje kod do review
  2. Dostaje zwrotkę: ten kod łamie SRP
  3. Odpowiadam: gdzie tu według ciebie jest złamane SRP?
  4. I się zaczyna g'wno burza na cały sprint

Oczywiście sytuacja jest czysto hipotetyczna i najczęstrza zwrotka jaką dostaję z review to: Nazwa za mało opisowa

3
jarekr000000 napisał(a):

Tak, to możemy sobie robić w ekonomii, teologii czy innych pseudonaukach.

Nie obrazisz inżynierii porównując ją do pseudonauki, bo inżynieria nie aspiruje do miana nauki, jest zupełnie innym rodzajem działalności. W odróżnieniu od nauki i pseudonauki inżynieria nie zajmuje się wymyślaniem twierdzeń lecz rozwiązywaniem praktycznych problemów.

Po drugie, to skoro nie pasują Ci zasady i prawa, a oczekujesz jedynie precyzyjnych twierdzeń, to nie pasuje Ci całość nauki poza matematyką - włączając w to np. fizykę.
Oczywiście można uznać, że jedynie matematyka jest nauką, ale nie wiem, czy taki fanatyzm do czegoś prowadzi. Matematycy to nie są jacyś kibole, nie pójdą na wydział fizyki albo budowy maszyn spuścić wpierdzielu konkurencji. :P

W IT nie mamy nawet papieża, który w razie czego wydałby interpretację - jeśli ktoś słabiej zrozumie ideę.

Ale to nie jest potrzebne, zasady to nie są dogmaty. Aczkolwiek myślę, że papieży w IT jest aż nadto.

Mętne definicje prowadzą do jałowych dyskusji i powodują, że zasady nie działają dobrze.

No nie wiem, taka zasada "posolić do smaku" jakoś się doskonale sprawdza, mimo że pozornie jest mętna.
Na pewno zamiast wkładać wysiłek w jałowe dyskusje dużo lepiej spożytkować tę energię na próby zrozumienia.

jarekr000000 napisał(a):

Nauki przyrodnicze bazują na precyzyjnych matematycznych modelach/definicjach. Te modele mogą się nie zgadzać z eksperymentami i zostac odrzucane, ale muszą być precyzyjnie zdefiniowane.

Wykorzystują, ale na pewno nie bazują. Z prostej przyczyny - matematyka zajmuje się abstrakcjami, nie opisuje świata materialnego, a obserwacja świata i eksperymenty często następują zanim się coś zamodeluje.

Zasada 20 linii wcale nie musi być słuszna (efektywna), to zupełnie nieistotne. Ale jeśli w danym projekcie przyjeliśmy taką zasadę, to możemy dość jasno określić, który kod ją łamie, a który nie. W odróżnieniu od SRP, gdzie kwestia łamania jest zależna od wiary oceniającego.

No i można stworzyć całkiem nieźle skopany kod trzymając się zasady n-linii, podobnie jak można mieć zupełnie nieprzetestowany kod z pokryciem 90%. W czym to lepsze od SRP, to nie wiem.

0
somekind napisał(a):
jarekr000000 napisał(a):

Tak, to możemy sobie robić w ekonomii, teologii czy innych pseudonaukach.

Nie obrazisz inżynierii porównując ją do pseudonauki, bo inżynieria nie aspiruje do miana nauki, jest zupełnie innym rodzajem działalności. W odróżnieniu od nauki i pseudonauki inżynieria nie zajmuje się wymyślaniem twierdzeń lecz rozwiązywaniem praktycznych problemów.

Po drugie, to skoro nie pasują Ci zasady i prawa, a oczekujesz jedynie precyzyjnych twierdzeń, to nie pasuje Ci całość nauki poza matematyką - włączając w to np. fizykę.
Oczywiście można uznać, że jedynie matematyka jest nauką, ale nie wiem, czy taki fanatyzm do czegoś prowadzi. Matematycy to nie są jacyś kibole, nie pójdą na wydział fizyki albo budowy maszyn spuścić wpierdzielu konkurencji. :P

W pełni popieram.
Inżynieria programowania jest "sztuką", tj teorią i praktyką ble, ble ble (pamiętam dyktowane do zeszytu na zajęciach z wojska)
Jako że jest sztuką, pewne zalecenia są i musza pozostać niejasne, miękkie, do stosowania przez rozsądek (jak wódka, która jest dla mądrych ludzi - cyt za pijusem ze skweru)

Natomiast w praktyce profesjonalnej ostatnich lat (10 lecie? więcej?) są pewne xxx...yzmy/izmy, oczekiwania, ze 100% się obstawi jakąś miarą, metryką, coś się czymś w 100% pokryje. Że zalecenia miękkie, kierunkowe, staną się twardymi itd ... stała lista AKURAT 21, 37 czy 43 wzorców (przy czy liczba warta świętej wojny)...

Że oczekiwanie jest, mniej się dziwię, masowe zatrudnienia ogromnych ilości ludzi, o różnej jakości, chciało by się przepuścić jakiś skrypt na repo i wiedzieć wszystko *)
(przy czym, na marginesie, geniusz niekoniecznie jest dobrym członkiem drużyny)

Wiara stricte religijna, a to już blisko do świętych wojen.

Jak "odkrycie" wzorców (hej, ludzie, zauważyliście że jak w branży stosowaliśmy X, to zwykle nam dobrze wychodziło Y) - bardzo niewiele jest wiedzy typu matematycznego, powstałej w umyśle z rękami w kieszeniach, a priori
Olbrzymia większość wiedzy tzw teoretycznej jest uogólnieniem "od praktyki" - a posteriori.
Most jest lepiej budować po prostej - ale jednak niektóre mosty są budowane po łuku i to sporym itd ...

*) wgrałem o jeden linter do VS za dużo , jak na razie mnie to śmieszy - jak mnie wq..i całkiem to odinstaluję. Właśnie coś takie. Bezduszny algorytm patrzy w kod i wie lepiej.

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