Klasa / funkcja mapująca (Mapper)

Niejednokrotnie zachodzi potrzeba zmiany jednej postaci danych do drugiej. Manualne tworzenie klas i ustawianie i właściwości "gdzie popadnie" może skończyć się źle, gdy zechcemy dodać nowe pole. Jak temu zaradzić? Sprawdź ten wpis.


Zamiana postaci danych

Klasa, bądź funkcja mapująca jest to kod odpowiedzialny za przerobienie jednego obiektu do postaci innego. W idealnym świecie powinna ona dodatkowo używać klas typu fabryka, aby tworzyć nowe, docelowe obiekty. Klasy mapujące są ostoją prostoty. Mogą posiadać kilka metod, które przyjmują pewien rodzaj obiektu i zwracają ten porządany. Jak zawsze, sposób użycia będzie się różnił od technologii i tym razem także od ortodoksyjności programisty, ponieważ naziści programowania powiedzą, że słuszniejsze z perspektywy czystego kodu będzie użycie fabryk i budowniczych. Zgadza się, jednak w kwestii czystości kodu trzeba mieć na uwadze także to, że powinien on być prosty w zrozumieniu. Nie sztuką jest stworzenie dwudziestu dodatkowych klas (fabryka + builder) dla dziesięciu encji, sztuką jest utrzymanie takiego kodu.

Przykład Mappera

Spójrzmy więc na ten przykład, który ukazuje najczęstszy case dla mapperów - zamiana wyniku logiki do postaci obiektu wyjściowego. W technologii, w której pracuję funkcja plainToInstance pochodzi ze specjalnej biblioteki transformującej i bardzo ułatwia życie. Może rodzić się teraz pytanie, to po co mi mapper, skoro mogę użyć wspomnianej funkcji? W celu uniknięcia problemów związanych ze zmianą, bądź zrezygnowaniem z tej biblioteki w przyszłości. Dodatkowo, w myśl zasad GRASP, powinniśmy posiadać miejsce, które najlepiej nadaje się do tworzenia danych obiektów i tylko w tym miejscu je tworzyć.

export class UserResponseObject {
  id: string;
  firstName: string;
  lastName: string;
  recommender?: UserResponseObject;
}

export class UserResponseObjectMapper {
  static mapUserEntity = (user: UserEntity): UserResponseObject =>
    plainToInstance(UserResponseObject, {
      ...user,
      recommender: plainToInstance(UserResponseObject, user.recommender),
    });
}

Klasa mapująca bez magii

Nie widzę przeszkód (i z drugiej strony w innych technologiach tak właśnie będą wyglądały mappery), aby stworzyć w ciele metody mapującej obiekt docelowy i manualnie ustawić wymagane pola. Pewnie wiele osób w tym momencie się oburzy, ponieważ złamałem conajmniej dwie zasady SOLID, a dodatkowo metoda mapująca jest statyczna. Zgadzam się, jednak dla mnie ważniejsze jest to, aby kod był prosty, poza tym refaktoryzacja tego fragmentu nie jest żadnym rocket-science. Wystarczy zwiększyć abstrakcję tej prostej operacji o kolejne klasy. W kwestii tego, że metoda jest statyczna, tutaj jest pewna słuszność i najprawdopodobniej gdyby nie fakt, iż w technologii w której pracuję testowanie metod statycznych jest bardzo proste, to użyłbym wstrzykiwania zależności dla klasy mapującej, a sama metoda nie byłaby statyczna. Warto także zwrócić uwagę, iż w innych językach programowania klasy te prawdopodobnie nazywać się będą fabrykami, jednak ogólnie rzecz biorąc mapper od fabryki różni się pod kilkoma aspektami, które postaram się w przyszłości (przy okazji omawiania wzorca factory) pokazać. 

export class UserResponseObjectMapper {
  static mapUserEntity(user: UserEntity): UserResponseObject {
    const response = new UserResponseObject();
    
    if (user.recommender) {
      response.recommender = UserResponseObjectMapper.mapUserEntity(user.recommender);
    }
    
    response.id = user.id;
    response.firstName = user.firstName;
    response.lastName = user.lastName;
    
    return response;
  }
}

Podsumowując




Polecane wpisy:

DRY: Don't Repeat Yourself

Zasada DRY, to po przełożeniu na język polski: nie powtarzaj się. Brzmi bardzo banalnie, jednak dosyć często okazuje się, że mamy problem z jej stosowaniem w kodzie. Dziś dowiesz się, jak nie łamać tej reguły, a co za tym idzie nie powtarzać się.

Sprawdź ten wpis

YAGNI: You aren't gonna need it

YAGNI to kwintesencja zasad clean code. Dotyczy ona bezużyteczności kodu, a dokładniej, konieczności usuwania tych fragmentów, które nie są potrzebne. W myśl "You aren't gonna need it" nie powinniśmy tworzyć niczego więcej, niż to, co jest potrzebne.

Sprawdź ten wpis

KISS: keep it simple, stupid!

Zasada KISS: "Keep it simple, stupid", może zostać dosłownie przetłumaczona na: "rób to prosto, głupku". Mówi ona o tym, abyśmy tworzyli kod w jak najprostszy i najbardziej czytelny sposób. Już dziś sprawdź, czego się wystrzegać, aby spełniać KISS.

Sprawdź ten wpis

Wzorce projektowe

Wzorce projektowe zostały stworzone po to, aby nie wymyślać przysłowiowego koła na nowo. Znajomość wzorców projektowych i umiejętność ich stosowania pozwala na szybkie rozwiązywanie problemów. Wpis ten radzi, jakie wzorce zastosować u siebie.

Sprawdź ten wpis

Wzorzec singleton

Kolejny kreacyjny wzorzec projektowy omawiany na łamach tego bloga: singleton. Wzorzec dookoła którego narosło wiele mitów i legend. Dziś o tym dlaczego singleton jest antywzorcem, jakie problemy powoduje oraz kiedy warto po niego sięgnąć.

Sprawdź ten wpis

Autor wpisu:

Gabriel Ślawski

Fanatyk czystego i prostego kodu. Zwolennik podejść DDD oraz Modular Monolith. Na codzień pracuje jako programista i architekt. Po godzinach spełnia się w projektach open source, udziela się na blogach oraz czyta książki o kosmosie i astrofizyce.