Po pierwsze - przykład jest całkiem bez sensu, więc nie bardzo jest materiał do dyskusji.
Co to za zmienna globalna wynik? Czemu to się roll nazywa jak nie ma tam żadnej losowości? Czy w ogóle kostka może mieć inne cechy niż liczba stron?
Abstrachując od tego - zarówno podejście obiektowe jak i funkcyjne są ok - to dwa równoważne paradygmaty w JS (choć u ciebie w drugiej wersji nie ma podejścia funkcyjnego, jest jakies nie wiadomo co na globalach, no chyba, że wynik to stała).
@Schizohatter
Właśnie o to chodzi, że funkcja jest dostępna wszędzie. To jest właśnie złe. Przez to mogą powstać konflikty nazw. Mówimy o tzw. enkapsulacji kodu.
No niekoniecznie dostępna wszędzie, w dobrym kodzie ograniczona do modułu / bloku kodu, wtedy konfliktów unikasz.
Dalej: mamy czytelniejszy kod. Grupowanie samo w sobie jest ważne dla struktury kodu. Zbiór luźnych funkcji mało mówi o działaniu kodu.
To mocno subiektywne, czy jest to czytelniejsze czy nie, może na poziomie pojedynczej klasy tak jest, ale jeśli chodzi o przepływ danych to polemizowałbym. Dodatkowo grupowanie nie jest unikatową cechą obiektówki.
Poza tym przypięcie "roll" do "Dice" jest "naturalne". Dzięki temu mniej musisz zapamiętać.
Kolejne subiektywne stwierdzenie, naturalne dla kogoś, kto ma do czynienia głównie z obiektówką, być może naturalne dla niektórych przpadków, niekoniecznie dla wszystkich, polecam: https://steve-yegge.blogspot.com/2006/03/execution-in-kingdom-of-nouns.html
Bo skąd mam wiedzieć, co jest argumentem funkcji roll? A tak to robię tylko dice.roll i mam co chcę.
Dokładnie z tego samego miejsca jak w przypadku metod przyjmujących argumenty - z czytelnej nazwy parametu i dokumentacji typów (+ ewentualnego komentarza) w JSDoc
Następnie jest kwestia tego, że w ramach jednego obiektu metody mogą wchodzić między sobą w interakcję wykorzystując zmienne obiektu jako nośnik informacji.
Równie dobrze można komponować funkcje przekazując informacje przez parametry.
Dzięki temu w momencie wykorzystywania obiektu nieistotna jest implementacja jego metod, tylko ich końcowy rezultat. Dopiero w momencie, kiedy coś w samym obiekcie mi nie odpowiada, wchodzę w jego plik (zazwyczaj osobny plik) i edytuję co trzeba.
W podejściu funkcyjnym też nieistotna jest implementacja funkcji, ba - jest kładziony mocny nacisk na to, by funkcja była "czysta" - czarna skrzynka bez efektów ubocznych, idealna do testowania. Zbiór powiązanych funkcji też zwykle znajduje się w jednym pliku-module.
No i ostatecznie obiekty umożliwiają w wielu językach czytelniejszy zapis metod dzięki tzw. chainowaniu:
np. roll(createDice(6)) -> Dice.new(6).roll(); [Ruby]
Nie powstaje w ten sposób "nawias-hell".
W JSie (a o nim mówimy), gdzie funkcje można przekazywać jako argumenty nie ma to większego znaczenia, bo funkcje można komponować równie czytelnie, co chainowac obiekty:
Zamiast pisać tak:
const doComplexThing = item => doBaz(doBar(doFoo(item))))
można użyć flow/pipe/compose:
const doComplexThing = compose(
doFoo,
doBar,
doBaz
)
To nie jest tak, że OOP daje większe możliwości. Ale jest pewną konwencją, która posiada swoje zalety, a do najważniejszych należą hermetyzacja (enkapsulacja), naturalność i czytelność.
Ma też trochę wad w stosunku do podejścia funkcyjnego, na szybko przychodzą mi do głowy:
- jest mniej komponowalne,
- często jest bardziej rozwlekłe,
- opieranie się o obiekty (dane + zachowanie) utrudnia dzielenie i rozpraszanie programu w porównaniu z operowaniem na strukturach danych,
- ciężej jest przeprowadzać generyczne operacje,
- flow programu jest skomplikowany.
Co do naturalności i czytelności - tak jak mówiłem - kwestia przyzwyczajeń i preferencji.
Twój post tak trochę wygląda, jakbyś porównywał obiektówkę z kiepskim proceduralnym spaghetti na globalach, a to nie jedyne opcje w JS ;)