Lambdy we wzorcach projektowych

0

Mam problem z lambdami w Javie. Używam ich robiąc operacje na listach korzystając z dostarczonych z ósemką interfejsów. Rozumiem, że są przydatne do obsługi optionala i np. przy łączeniu action listenerów z metodami robiąc jakąś aplikację desktopową w Swingu. Natomiast poza tymi zastosowaniami związanymi z gotowymi bibliotekami nie bardzo wiem gdzie miałbym ich używać. Pomyślałem, że inne zastosowania mogą być związane ze wzorcami projektowymi i znalazłem takie coś:
https://medium.com/analytics-vidhya/simplify-strategy-using-lambda-expression-40195d1445ea
Jeśli chodzi o pierwszą wersję kodu wszystko jest jasne. Interfejs nad sortowaniem i wyszukiwaniem zwiększył elastyczność kodu i można bez zmiany kontekstu rozszerzać aplikację o nowe tryby sortowania i wyszukiwania. Natomiast w drugiej wersji likwidacja modeli na rzecz lambd znajdujących się w kliencie wg mnie ma sens tylko, jeśli się zakłada że aplikacja nigdy się za bardzo nie rozszerzy, a same metody będą nieduże. Mylę się tutaj? Przy okazji może powiedzcie do czego wy ich używacie poza oczywiście filtrowaniem kolekcji itp.

0

@paranoise:

link nie działa
Może być jak mówisz. Zaraz zostanę advocatus diaboli

Mogą być sytuacje, że klasyczny kod większej wielkości nie zyskuje na przepisaniu na lambdy (sam w sobie). Lamby małe, przy gwarancji czystości danych, jasny cel w momencie pisania *) są całkiem fajne.

Mnie irytuje niedebugowalność lambd. W klasyku, jak masz problem z 16tym powtórzeniem danej wśród 10 tysięcy, nawet dodasz tymczasowego ifa, aby się na to zaczaić.

*) to chyba o wyrażeniach regularnych się mówi w branży, że są write-only. Jakakolwiek próba rozwinięcia sprawia, że lepiej zaorać, i zrobić od zera.

2

Nie rozumiem, w jaki sposób zastosowanie lambd ogranicza rozszerzalność kodu? Dzięki lambdzie nie trzeba pisać boilerplate związanego z klasa anonimową tam, gdzie wymagany jest functional interface. Tak wiec dalej można dostarczać dowolna implementacje + można to zgrabniej zapisać.

1

Zacznijmy od podstaw bo ja tutaj widzę pewne niezrozumienie. Celem wzorca strategii nie jest wykorzystanie lambd. Wzorzec ten istnieje po to, żeby móc dostarczać zachowanie z zewnątrz. Lambdy nie zastępują niczego, a jedynie upraszczają zapis. Tzn. mając metodę:

public static String myToString(MyModel model, MyModelStrategy strategy) {
   return "Aggregating -> " + strategy.map(model);
}

Można tego użyć na kilka sposobów:

class MyModelStrategyLocalImpl extends MyModelStrategy {
   public String map(MyModel model) {
      return "Result";
   }
}

private static String localToString(MyModel model) {
   return "Result";
}

public static void execute(MyModel model) {
   myToString(model, e -> "Result");
   myToString(model, MyClass::localToString);
   myToString(model, new MyModelStrategyLocalImpl());
}

Wszystkie zapisy będą (mniej-więcej, bez zagłębiania się w bytecode) równoważne - i jak pewnie zauważysz, zapis przez lambdę jest najzwięźlejszy z wszystkich, później przez odwołanie do metody, a na końcu mamy własną implementację. Oczywiście w zależności od stopnia skomplikowania ta kolejność może się zmienić. Ja na przykład wyznaję uważam, że jeśli konkretna implementacja strategii ma jakiś własny stan to lepiej stworzyć całą klasę (nawet wewnętrzną).

Jeśli chodzi o artykuł (działający link: https://medium.com/analytics-vidhya/simplify-strategy-using-lambda-expression-40195d1445ea ) to przykład z zamianą implementacji sortowania z jawnej na lambdę (czyli koleś usunął klasę BubbleSortStrategyImpl i zamiast tego zapisał to jako Consumer<List<String>>) jest moim zdaniem złym użyciem lambdy i chyba nigdy bym tego w ten sposób nie zrobił. To jest poprawny sposób na korzystanie z strategii, ale użycie lambd w tym wypadku nie upraszcza zapisu - a go komplikuje, co zresztą byłoby widać gdyby w artykule autorowi chciało się to zaimplementować.

Jeśli chodzi o pytanie - kiedy używam lambdy - to odpowiedź jest prosta - kiedy się da, chyba, że się nie da to wtedy nie używam. Tzn. pisząc, zawsze patrzę na kilka rzeczy, tj.

  • czy potrzebny jest wewnętrzny stan operatora (jeśli tak to wolę to jednak zapisywać to w formie klasy - bo zapis lambdy z stanem średnio wygląda)
  • czy istnieje ryzyko, że kod wypłynie poza scope funkcji (jeśli tak to wolę to jednak zapisywać to w formie klasy - bo nie chcę, żeby ktoś potem w logach zobaczył wyjątek z lambdą w roli głównej)
  • czy kod będzie czytelny (lambdy są dla mnie raczej jednolinijkowcami, jeśli muszę mieć więcej niż jedną linijkę to wydzielam prywatną metodę statyczną i używam odwołania do metody lub po prostu piszę oddzielną klasę)
  • czasami frameworki pełne refleksji mają także swoje preferencje

Oczywiście są to takie ogólne zasady, które mogą ustąpić szczególnemu zdrowemu rozsądkowi.

4

Lambda to nic innego jak cukier składniowy na instancję klasy anonimowej z jedną nadpisaną funkcją.

i jak wszystkie cukry składniowe - nijak się to nie przekłada na jakiekolwiek cechy projektowe.

1 użytkowników online, w tym zalogowanych: 0, gości: 1