- małe
- wykonywać jedną czynność
- zachowywać jeden poziom abstrakcji
O ile pierwsze dwa punkty łatwo sobie wyobrazić i zaimplementować, o tyle zauważyłem, że ostatni punkt jest nie do końca zrozumiały wśród niektórych programistów.
Aby unaocznić gdzie popełniany jest często błąd użyjmy prostej i każdemu znanej życiowej czynności:
Przepis na jajecznicę
- Rozpuścić masło na małej patelni
- Wrzucić na stopione masełko szynkę pokrojoną w małe kwadraciki oraz drobno posiekany szczypiorek
- rozbić ostrym nożem skorupki jajek i zawartość wylewać na patelnię
- Dodać trochę soli oraz pieprzu i mieszać, aż do momentu ścięcia się jajek
Przepis jest intuicyjny i raczej nikt nie powinien mieć problemu z jego zrozumieniem. Na tym poziomie abstrakcji wystarczy użyć kilku składników w prosty sposób i już mamy nasza potrawę gotową.
Jednakże "pod spodem" mają miejsce skomplikowane i (ze względu na nasz kulinarny model domenowy ) nieistotne zjawiska. Jak mógłby powyższy przepis wyglądać gdyby był częścią programu tworzonego przez nierozgarniętego programistę?
dodaj(maslo)
while(maslo.nieJestStopione){
for( atom in atomyWProbceMasla){
atom.dostarczEnergii
}
}
dodaj(pokrojona Szynka)
while(szczypiorek.jestCaly){
oddziaływuj nożem na sieć krystaliczną szczypiorku
}
dodaj(szczypiorek)
noż.dodajEnerigiiPotencjalnej
noz.zamieńEnergięPotencjalnąNaKinetyczną
noż.uderzW(jajko)
dodaj(zawartośćJajka)
dodaj(sól)
for(ziarnkoPieprzu in szczyptaPipeprzu){
dodaj(ziarnkoPieprzu)
}
i mieszać, aż do momentu ścięcia się jajek
(poprawność kwestii fizycznych może być niedokładna ale nie to jest meritum przykładu)
Tak zmieniony przepis o wiele trudniej zrozumieć i co gorsza domena kulinarna została zmieszana z domena fizyczną przez co potencjalny odbiorca przepisu musi zmierzyć się ze znacznie większą płaszczyzną problemu.
Aby zachować jeden poziom abstrakcji wracamy do korzeni:
patelnia.dodaj(maslo)
patelnia.podgrzejDoRostopieniaMasla()
patelnia.dodaj(pokroj(szynka))
patelnia.dodaj(pokroj(szczypiorek))
patelnia.dodaj(rozbij(jajko))
patelnia.dodaj(sól)
patelnia.dodaj(pieprz)
mieszajDoMomentuScieciaSieJajek(patelnia)
Poprawione źródło przepisu jest czytelniejsze i nie wymaga znajomości zagadnień wykraczających poza domenę kulinarną. Widzimy także ciekawy efekt wizualny, który może być użyty jako swoisty test czy nie wykraczamy poza dany obszar abstrakcji : mianowicie wersja poprawiona nie ma wcięć.
Jak przedstawione zjawisko mogłoby wyglądać na przykładzie bardziej zbliżonym do IT?
funkcja(obiekt){
operacjaDomenowa();
bebechy=obiekt.wyciągnijBebechy
...
kilka zagnieżdzonych pętli i try-catch
...
operacja domenowa()
znowuGrzebanieWBEbechach()
}
przykład konkretny : obsługujemy request od przeglądarki, który może zawierać obrazki i załączniki tekstowe. Każdy typ załącznika musimy zapisać do osobnego katalogu.
przetworzRequest(request){
zapiszPlikiTekstowe(request)
zapiszObrazki(request)
}
A można i tak :
przetworzRequest(request){
zapiszPlikiTekstowe(request)
strumien=OtworzStrumien
for(bit in obrazek){
strumien.pisz(bit)
}
strumien.zamknij
}
Chyba jest jasne,która wersja będzie czytelniejsza,przyjemniejsza,wygodniejsza i łatwiejsza w utrzymaniu osobie, która ujrzy ów fragment po raz pierwszy w życiu.
Właśnie czytam tą książkę i też nie rozumiałem wytłuszczonego podpunktu. Ten wpis się przydał. :)
OdpowiedzUsuń