Jak nazywać?

Jestem zwolennikiem krótkich nazw. Niech nazwa funkcji czy klasy będzie jednym słowem. Czasem dwoma. Klasy o nazwach typu ObjectFactoryCreatingFactoryBean łamią moje poczucie stylu.

Parametry też funkcja powinna mieć w niewielkiej liczbie. Jeśli można, winno być to zero. Jeśli chcemy, możemy dodać również parametr czy dwa. Jednak dalsza ich liczba to przesada*. Ciężko zapamiętać wtedy kolejność. Lepszym rozwiązaniem byłoby wtedy przekazanie tablicy albo obiektu.

Uzasadnionym wyjątkiem być może mogłaby być funkcja, która działałaby jak `console.log` - to znaczy brałaby wszystkie końcowe argumenty i robiła z nimi dokładnie to samo.

* dodane 3.10.2017: no, może trzy parametry jeszcze ujdą - nie chodzi o to, żeby się kurczowo trzymać liczby parametrów, tylko o to, żeby nie robić bałaganu. Czasem nawet 4 parametry będą ok, np. moglibysmy mieć createRectangle(x, y, width, height) i byłoby to dalej intuicyjne. Jednak nawet wtedy rozważyłbym api createRectangle({x: 0, y: 0, width: 100, height: 100}),  ponieważ wtedy można byłoby wszystkie parametry wrzucić do zmiennej np. tak:
const rect = {x: 0, y: 0, width: 100, height: 100};
createRectangle(rect);

Lubię też spójne nazewnictwo.

Jeśli w projekcie do dodawania elementów `y` do `x` służy metoda `x.add`, to nie powinno w innej klasie być to `insert` (o ile oczywiście nie czynimy jakiegoś większego i celowego rozróżnienia między tymi dwiema rodzajami operacji).

Nie lubię też nadmiarowych metod.

Np. stringi w JavaScript mają trzy różne metody, które robią prawie dokładnie to samo: `substr`, `substring` i `slice`. O ile znaczenie parametrów trochę się różni - to wszystkie metody robią to samo - tworzą nowy napis, który jest wycinkiem starego. Wystarczyłaby więc tylko jedna metoda (i tą metodą powinna być   `slice`, ponieważ wtedy zachowanie stringów byłoby spójne z tablicami, które też udostępniają metodę `slice` do tworzenia wycinków danej tablicy).

Interfejs EventEmitter w NodeJS również jest zaprojektowany źle. Za dużo jest w nim metod. Powinny być tylko dwie: `emit` i `on`, czyli te, które realizują odpowiedzialność EventEmittera - funkcja `emit` pozwala na "emitowanie" eventów, a funkcja `on` pozwala na eventów tych nasłuchiwanie. Bez większości pozostałych metod można się spokojnie obyć (kto z was używał kiedykolwiek metody `prependOnceListener`)?

Można się jeszcze zastanawiać nad metodami `removeListener` i  `once` . Tu też jest pewna duplikacja, bo jeśli mamy metodę do usuwania listenerów, to tworzenie dodatkowej metody `once` jest już zbędne (once można w końcu osiągnąć tak, że listener sam się usunie przy pierwszym wywołaniu).

Możecie spytać, czy ma to w ogóle jakieś znaczenie? Ano ma, ponieważ im więcej niepotrzebnych metod ma dana klasa, tym ciężko zrobić do niej kompatybilną do niej alternatywę (która będzie np. bardziej wydajna). Jeśli ktoś chce zrobić alternatywną klasę EventListener, to musi albo implementować wszystkie te głupie funkcje, albo jego alternatywna biblioteka nie będzie w pełni kompatybilna z EventEmitterem z NodeJS.

Powracając do usuwania listenerów, zastanawiam się czy nie lepszy byłby wzorzec dispose, coś jak to:

const dispose = ee.on('foo', () => {
    console.log("event foo was emitted.");
    dispose(); // we don't want to listen anymore
});

ee.emit('foo');


Wtedy ani `once` ani `removeListener` nie byłyby juz potrzebne.

Jest oczywiście mnóstwo innych przykładów na to, że istniejące interfejsy różnego rodzaju API (nazwy metod, sposób ich wywołania itp.) są zaprojektowane słabo, wadliwie.

Pewnych rzeczy nie da się już zmienić - np. nie da się zlikwidować metod `substr` i `substring` ze stringów w JavaScript, gdyż rozwaliłby się cały Internet i wszystkie skrypty, które by z tego korzystały, przestałyby działać.

Więc jeśli jesteśmy na etapie projektowania naszej biblioteki i chcemy wypuścić w świat pierwszą wersję - zastanówmy się dwa razy czy warto się z tym śpieszyć. Pierwsza wersja i tak zwykle jest skopana. Dlatego większość popularnych bibliotek JavaScript gdzieś około drugiej, trzeciej czy czwartej wersji przechodzi całkowitą transformację. Twórcy wprowadzają zmiany niekompatybilne wstecz! I jeśli biblioteka jest wypuszczona w świat i zdobyła sobie już grono użytkowników - to jej użytkownicy muszą potem przerabiać kod wraz z aktualizacją biblioteki, tutoriale stają się nieaktualne, dodatki przestają działać itp. Bywa to bolesne.

Dlatego warto zarezerwować sobie czas na design, na prototypowanie różnych rozwiązań. Dopóki coś nie jest wypuszczone w świat*, to masz o wiele więcej swobody twórczej na eksperymentowanie i na dopracowywanie swojego API. Potem może być już to trudniejsze. Nie tylko z powodu większej popularności, ale również takiej, że sam projekt może być większy - łatwiej zmienić coś w dwóch klasach na krzyż niż w stu różnych plikach.

Nie znaczy, że nie można tego zrobić - pewnym rzeczom przydałaby się taka radykalna zmiana. Jednak i wtedy najlepiej jest jednak pomyśleć o istniejących użytkownikach - np. twórcy Reacta udostępnili narzędzia, które same refaktoryzowały kod ze starej do nowej wersji Reacta, kiedy przeprowadzali tego typu zmiany. Albo Python. Pamiętam, że jak się pojawiła trójka, to przez długi czas dwójka była dalej wspierana (może dalej jest? Nie orientuję się), więc można było poczekać z przejściem na nową wersję, nie śpieszyć się.


* albo jest, ale jeśli jeszcze nikt z tego nie korzysta - np. jeśli coś wypuścisz na npm, ale masz zero pobrań dziennie. 








Komentarze

  1. Eeee tam. Tak to się dawno temu projektowało w Java i C++ - im mniejszy interfejs tym lepsze życie. Natomiast w JS plusem jest społeczność. Im więcej oczu patrzy tym szybciej idzie wypracować lepsze API. Z resztą z angularem też tak było.

    OdpowiedzUsuń
  2. Tzn. dawno temu, tzn. jak się programuje teraz w Javie?

    Mnie się ona kojarzy właśnie z overengineeringiem i przerostem różnego rodzaju interfejsów nad treścią (więc i klasy typu "ObjectFactoryCreatingFactoryBean"). Chociaż nie programuję w Javie, więc nie wiem na ile się na codzień korzysta z takich cudów.

    OdpowiedzUsuń
  3. Społeczność to miecz obosieczny. Często pomaga, ale często również powoduje, że pewnego rodzaju schematy są nie do ruszenia, bo "tak się zawsze robiło" i ludzie się przyzwyczaili (albo naśladują po prostu modę).

    Mało kto chce lepszego API - bo starym wyjadaczom zwykle wystarcza stare API, które dobrze znają, a nowicjusze z kolei nawet nie myślą jeszcze o tym, bo są na etapie pochłaniania wiedzy i nauki podstaw.

    OdpowiedzUsuń

Prześlij komentarz

Popularne posty z tego bloga

Dlaczego nie da się nadgonić frontendu

Absurdy Rekrutacji 2023

Przygody juniora (1)