Przejdź do treści

Zadanie rekrutacyjne 🤫 [MID] – TDD 🤷‍♂️❓

Cześć. Dziś przedstawię Ci temat o którym wspominałem 2 tygodnie temu tutaj – czyli zadanie praktyczne jakie dostałem na rozmowie rekrutacyjnej.


Treść zadania brzmiała mniej więcej tak: Tu masz interfejs, tu jest klasa z testami, zaimplementuj to tak żeby testy przechodziły. Ogólnie chodziło o to aby napisać implementację która zlicza częstotliwość wystąpienia danego znaku w wyrazie i zwracać to jako mapę Map<Character, Double>, np:

  • a -> map.get(a) -> 1
  • ab -> map.get(a) -> 0.5 / map.get(b) -> 0.5
  • aaa -> map.get(a) -> 1
  • AaA -> map.get(a) -> 1
  • abcd -> map.get(a) -> 0.25

Interfejs:

Klasa testowa: (gist)

Jeżeli chodzi o samą klasę testową to nie zapamiętałem wszystkich testów jakie tam były (+ ich nazw dokładnie 1 do 1), tutaj przedstawiam większą i ważniejszą część którą zapamiętałem.


1. Pierwszym krokiem jaki zrobiłem było utworzenie klasy implementującej nasz interfejs oraz zwrócenie go w teście. Nie miałem pomysłu na nazwę więc poleciała pierwsza lepsza jaka mi przychodziła do głowy FrequencyImpl. Teraz jak na nią patrzę to na pewno dałbym inną, np. FrequencyAnalyzer albo FrequencyCharacterAnalyzer.

Zgodnie z TDD pierwsze testy uruchomiłem od razu po utworzeniu domyślnej implementacji. Oczywiście wszystkie zakończyły się błędem 😅 ale o to chodzi w TDD – krótka i szybka pętla zwrotna. Następnie, czas na spełnienie pierwszego testu – rozwiązałem to tak, że stworzyłem mapę i na sztywno wpisałem wartość jaka ma być zwracana – czy to była dobra decyzja? I tak i nie. Tak, bo miałem znów bardzo szybki zwrot z informacją, że pierwszy test się udał a nie bo wiedziałem, że za chwilę i tak to zmienię więc czy dla spełniego takiego testu jest sens pisać tak PROSTĄ IMPLEMENTACJĘ?? W moim odczuciu – tak, z uwagi na pętlę zwrotną z feedbackiem że coś działa + o to właśnie chodzi w TDD – piszemy coś najprościej jak się da a później refaktorujemy, weryfikując że testy nadal przechodzą.

No i dobra. Pierwszy test zaliczony, czas przejść do drugiego.

2. Mamy zwracać równe połówki gdy jako wejście dostaliśmy dwa różne znaki. Właściwie nie trzeba się nad tym za długo zastanawiać jeżeli chcemy osiągnąć cel jak najprostszym i najszybszym sposobem. Możemy wyciągnąć tablicę znaków z naszego inputa a później po niej iterować. Każdy napotkany znak wsadzamy do mapy, gdzie jako wartość robimy proste obliczenie arytmetyczne dzieląc 1 przez ilość znaków.

Do tej pory wszystko było git. Ale teraz zaczęły się schody. I to takie których nie przeszedłem – właściwie nie wiem dlaczego.. chyba za bardzo się zestresowałem takim live codingiem. Bo żeby nie było – do wszystkiego co pisałem często były zadawane pytania – dlaczego tak, a po co to? No nieważne… jesteśmy przy drugim teście odpalamy i jest zielono (przy pierwszym i drugim teście) – lecimy do kolejnego.

3. Mamy ignorować powtórzenia, czyli w świecie baz danych musimy zrobić distinct na naszym wejściowym stringu. Potencjalnie można ten test rozwiązać na kilka sposobów a najprostszy z nich wyglądałby tak, że robimy 2 oddzielne pętle. W pierwszej wsadzamy elementy do mapy, pamiętając, że mapa w kluczu nie przechowuje duplikatów (tutaj jako wartość możemy wsadzić dowolny element będący doublem – nie ważne czy to będzie prawidłowa wartość czy nie), w drugiej natomiast (wiedząc, że nasza mapa zawiera unikatowe klucze – czyt. zrobiła distinct na stringu wejściowym) wsadzamy wartości do mapy raz jeszcze – tym razem przypisując każdemu znakowi wejściowemu prawidłową wartość – czyli dzieląć 1 przez wielkość mapy.

Gdybym tak to zrobił, miałbym zaliczony test 3, 4 i 5.

4. Ingorujemy małe i wielkie znaki. Najprostsze rozwiązanie? Na całym wejściowym stringu robimy .toLowerCase().

Testy 6 i 7 zaliczone.

5. Ostatni test do zaliczenia. Bardzo prosty – wystarczy dodać obsługę wartości nullowej na wejściu. Końcowo nasza implementacja wygląda tak:

Wszystkie testy przechodzą. Zadanie zaliczone. I tutaj każdy kto pracuje z TDD powinien powiedzieć hola hola amigos 👒!! Chyba brakuje jeszcze jakiejś fazy. Właściwie to powinna ona być robiona po każdym z zaliczonych testów – tzn. refactoring. Niestety na rozmowie nie udało mi się dojść do tego etapu, jednak tutaj zaprezentuję rozwiązanie jakie byłbym w stanie wrzucić na review gdybym mógł pomyśleć o tym zadaniu na chłodno.

Ostateczne rozwiązanie: (gist)


Tak to wyglądało. Trudne, łatwe? Powiedziałbym – takie przeciętne. Myślę, że w tym zadaniu chodziło o sprawdzenie kilku aspektów:

  1. Znajomość i umiejętność pracy z TDD
  2. Znajomość API Javy
  3. Umiejętność logicznego myślenia
  4. Radzenie sobie ze stresem

Daj znać w komentarzu co byś napisał inaczej (jak wyglądałoby Twoje ostateczne rozwiązanie)A może sam czasem przeprowadzasz rozmowy rekrutacyjne to co byś dodał do tego zadania? Z chęcią będę aktualizował ten post – a nóż widelec może przyda Ci się to zadanko przy prowadzeniu przyszłej rozmowy rekrutacyjnej… 😉


Tyle na dziś. Dzięki bardzo za przeczytanie. Za tydzień – standardowo, w sobotę o 12 –  obsługa VIMa dla każdego. Czyli w 5 minut naucz się tego dziwnego edytora!

0 0 votes
Article Rating
Subscribe
Powiadom o
guest
4 komentarzy
najnowszy
najstarszy oceniany
Inline Feedbacks
View all comments
Paweł
Paweł
1 rok temu

BTW ten link u góry strony pod stringiem 'tutaj’ nie działa (ten https://www.bdabek.pl/2020/02/15/pytania-rekrutacyjne-mid-01/)

Paweł
Paweł
1 rok temu

 @Override
 public Map<Character, Double> analyze(final String input) {
  if (input == null) {
   throw new IllegalArgumentException();
  }
  return input.toLowerCase().chars()
    .distinct()
    .mapToObj(e -> ((char) e))
    .collect(groupingBy(identity(), collectingAndThen(counting(),
      count -> (double) count / input.toLowerCase().chars().distinct().count())));
 }

Rozwiązanie które udało mi się napisać dzięki pomocy i wykładom Pana Venkata Subramaniam. Ogólnie polecam jego wykłady na YT bardzo mocno, i bardzo polecam przejście z imperative code style na declarative code style. Kod staje się duże czytelniejszy, mniej jest accidental complexity. Choć ten przykład tego nie pokazuje aż tak bardzo jednak w większości przypadków jest to widoczne od razu. Pozdrawiam i czekam na kolejne zadania 🙂

4
0
Would love your thoughts, please comment.x