Łańcuszki, czyli czytelny kod z Method Chaining

By | April 26, 2016

Wyślij ten łańcuszek jednemu znajomemu. Ja wysłałem i mi się skompilowało, Krzyś nie wysłał i … dowiedział się dlaczego warto robić backupy. Ale nie zacznę powrotu po dwutygodniowej przerwie spamem. Zamiast tego napiszę kilka słów o tym co każdy z Was pewnie już widział w wielu elementach oferowanych przez .NET out of the box, a co sprawia, że kod staje się naprawdę czytelny i może być zrozumiały nawet dla osoby, która kilka dni wcześniej dowiedziała się, że programuje się nie tylko pralki. Mowa będzie o tworzeniu “łańcuchów” wykonywanych metod zwanym również method chaining i fluent interface.

O czytelnym kodzie napisano już wiele i nawet ktoś z tak niewielkim doświadczeniem jak moje wie, że jego czytelność jest ważna. Z samym method chainingiem stykałem się już wcześniej, ale nigdy nie wprowadzałem go do swoich klas, zwyczajnie jakoś nie przyszło mi to do głowy. Pewnego dnia natrafiłem na wzmiankę o tym jak to zrobić i umieściłem go eksperymentalnie w swoim projekcie na “Daj się poznać”. Jak tylko zobaczyłem efekt końcowy, to wykorzystałem method chaining do tworzenia nowych obiektów w kilku innych miejscach jako swego rodzaju builder. Na potrzeby tego posta posłużę się przykładem spoza tego projektu, ale łancuszki już w nim zagościły i będę się starał, by było ich coraz więcej.

Głównym zastosowaniem do jakiego ja używam chainingu, to tworzenie różnego rodzaju builderów, które wymagają włożenia do nowego obiektu sporej ilości danych na wejściu. Zdecydowałem się na użycie danych z nieco innego projektu, bo występuje tam klasa Room, która potrzebuje wypełnienia kilku pól. Wygląda ona w ten sposób:

Gdybyśmy chcieli użyć zwykłego konstruktora do stworzenia nowego obiektu Room i wypełnić wszystkie jego właściwości, to wyglądałby on mniej więcej tak.

Nawet przy założeniu, że nazwy zmiennych są czytelne i mówią wszystkie, to nie wygląda zbyt rewelacyjnie. Oczywiście możemy spróbować czegoś takiego:

Jest to już nieco czytelniejsze i jaśniejsze, ale wciąż pozostawia trochę miejsca do poprawy. W projekcie, z którego wyciągnąłem kod, tworzenie obiektu Room wygląda następująco.

Odrobinę zmieniłem wartości zmiennych przyjmowanych przez metody, bo w rzeczywistej implementacji wyglądało to nieco inaczej, ale musicie przyznać, że różnica jest widoczna na pierwszy rzut oka. Co robi ten blok kodu? Tworzy pomieszczenie o numerze n, z nazwą x i tak dalej. Bardziej oczywiste mogłoby to chyba być tylko wtedy, gdybym nazwy metod zmienił na polskie.

A jak to działa?

Method chaining jest bardzo prosty do wprowadzenia i polega tylko i wyłącznie na odpowiednim nazwaniu poszczególnych metod i dopilnowaniu, by zwracanym przez nie typem był dokładnie ten sam typ, którego obiektu dotyczy metoda, a zwracaną wartością musi być this. Oto kod mojego buildera (wyciąłem kilka metod, bo tak naprawdę działają w ten sam sposób), a tuż pod nim krótkie wyjaśnienie działania tego procesu.

Po wywołaniu metody statycznej klasy RoomBuilder.BuildRoom() tworzony jest świeżutki obiekt RoomBuilder, który zawiera w sobie pole z równie świeżutkim obiektem Room. Wszystko co umieścimy po kropce za nawiasem, będzie dotyczyło stworzonego właśnie obiektu. W praktyce więc w tym miejscu RoomBuilder.BuildRoom().WithNumber(n) wykonywana jest metoda na nowo stworzonym RoomBuilderze i sprawia, że umieszczony w nim pokój nie znajduje się już w stanie surowym, a doczekał się własnej nazwy.

Jeśli na samym końcu doczepimy jeszcze jedną kropkę i tuż za nią kolejną metodę, to będzie ona wywoływana na obiekcie RoomBuilder, który został już zmodyfikowany przez metodę WithNumber(). Dzięki temu, że zwracaną wartością tych metod jest this, każdy taki blok kodu, może być traktowany jako obiekt na poszczególnych “etapach budowy” i możemy łączyć ze sobą metody w długie, ale i czytelne łańcuchy znaków.

Pod sam koniec łańcucha pozostało mi tylko wywołać metodę Build(), która sprawdza czy wszystko jest na swoim miejscu i zwraca obiekt Room, nad którym w pocie czoła pracowaliśmy.

 

Podbij ↑