Dependency injection vs strategy

0

Czy wstrzyknięcie interfejsu repozytorium do serwisu aplikacyjnego można nazwać jako przykład wykorzystania wzorca strategii? W końcu pozwalamy klientowi serwisu zdecydować, czy chce użyć do zapisu danych InMemoryRepository czy PostgresRepository. Zainspirowane ostatnim wątkiem :)

2

Wydaje mi się, że ten podział jest symboliczny - choć obydwa wzorce są do siebie podobne w realizacji, to intuicyjnie istnieje pewna różnica między wstrzykiwaniem zależności (które kojarzy się wysoko-poziomowo), a wzorcem strategii (które kojarzy się nisko-poziomowo i w praktyce częściej implementowane jest przez przekazywanie bytów polimorficznych do argumentów "zwyczajnych" funkcji czy metod, a niekoniecznie już konstruktorów).

Jak to zwykle bywa, wszystko jest na oko - odpowiadając wprost na pytanie powiedziałbym: tak - wstrzykiwanie zależności jest w gruncie rzeczy tym samym, co strategia :-)

1

Czy wstrzyknięcie implementacji interfejsu repozytorium do serwisu aplikacyjnego można nazwać jako przykład wykorzystania wzorca strategii?

Nie / zależy, bo DI nie zawsze pozwala ci zmienić algorytmu w runtime, a zatem nie musi spełniać założeń strategy pattern

Trzeba doprecyzować

Constructor injection? method injection? property injection?

A może jednak DI + jakiś kontener IoC?

0

Strategia to po prostu interfejs. DI może realizować to co strategia ale nie musi. Nic nie szkodzi, żeby przekazywać różne instancje tej samej klasy, wtedy metody są wspólne. Przykładowo mając Splitter.on(',') przekazujemy string, jakby Java była językiem, gdzie tablica w stringu jest publiczna to miałbyś DI w którym nie wykorzystujemy żadnej metody

1

@nobody01:

Dlaczego DI nie pozwala w runtimie zmienić? DI to tylko wstrzykiwanie zaleznosci

Jeżeli utworzysz obiekt MaszynaSortujaca i wrzucisz do niego przez constructor injection BubbleSortAlgorithm, to jak chcesz go podmienić?

0

A po co go podmieniać? Klient wybiera implementację, tworzy obiekt i tyle. Niemutowalnosc.

Chociaż w tym wypadku można by wstrzyknąć przez metodę algorytm.

Czyli jako różnice między DI a strategy uważamy możliwość zmiany zależności w już istniejącym obiekcie?

0

W moim rozumieniu strategia ma różnicować zachowanie pomiędzy obiektami tej samej klasy. Pierwszy z brzegu przykład jaki mi przychodzi, to gra, np. taka//play.google.com/store/apps/details?id=com.aso.tdf2012int

Gracz kieruje tam drużyną kolarzy i ustawia im zużywaną moc.
W grze "uczestniczy" też ileś tam NPC mających różne cele ustalane przez "trenera", w zależności od celu drużyny i roli konkretnego zawodnika w drużynie
Każdy NPC, to ta sama klasa, ale jak widać wyżej, ma kierować się specyficzną logiką. Załóżmy, że jest dostaje jakieś dane na wejściu (sytuacja wyścigu) i do określenia jest wyłącznie % mocy jaką zawodnik ma generować. Czyli uproszczona klasa zawodnika to:

public class Npc{
private int stamina;
private PlayerAi playerAi
......
  public float calculateCurrentPerformance(RaceState raceState){
    return playerAi.calculateCurrentPerformance(raceState)
  }
}

gdzie PlayerAi to dla uproszczenia interface funkcyjny:

public interface PlayerAi{
  float calculateCurrentPerformance(RaceState raceState)
}

Do tego mamy różne implementacje tego Ai, np. oparte o jakieś sieci neuronowe wyliczenia co najlepiej robić, żeby osiągnąć wyznaczony przez trenera cel.
W zależności od sytuacji druzyny, trener ustawia różne priorytety dla "zawodnika", poprzez wstrzyknięcie odpowiedniej implementacji.

Nie wiem jak to jest zrobione w zeszłorocznej edycji, ale parę lat temu było zaimplementowane mniej więcej tak jak napisałem wyżej.

Przykład wybrany z myślą o @renderme, bo programowanie to nie tylko front i back end ;)

0

@nobody01:

Czyli jako różnice między DI a strategy uważamy możliwość zmiany zależności w już istniejącym obiekcie?

no właśnie się zastanawiam - nie.

To inaczej, czy jeżeli uznamy że DI = przykład strategii, to coś zyskamy na tym?

Jeżeli nazwiemy refleksję taką jakby strategią, bo przecież można nią dużo namieszać / podmienić, to też coś zyskamy?

1

wg mnie porównujesz japka z gruszkami.

celem Dependency Injection jest Inversion of Control.
celem strategii jest zapodawanie odpowiedniej implementacji w zależności od kontekstu.

5
slsy napisał(a):

Strategia to po prostu interfejs.

Strategia to rodzina wymienialnych algorytmów o wspólnym kontrakcie. To że definiuje go interfejs to jest akurat najmniej istotna część.

nobody01 napisał(a):

A po co go podmieniać? Klient wybiera implementację, tworzy obiekt i tyle. Niemutowalnosc.

Bo na tym polega ten wzorzec. Klient używający strategii może raz wykorzystywać jedną implementację, raz inną.

Czyli jako różnice między DI a strategy uważamy możliwość zmiany zależności w już istniejącym obiekcie?

Nie, różnica jest taka, że strategia używa DI, a DI nie używa strategii.

0

Jeżeli spojrzy się na właściwie każdy wzorzec projektowy, to równie dobrze można powiedzieć "to taka inna metoda main()". Strategia to nie jest ani "takie DI", ani "takie lambda", chociaż zarówno DI, jak i lambdy można użyć do implementacji strategii.
Istotą wzorca jest to, że:

  • mamy jakąś klasę z logiką

  • część tej logiki ma być inna pomiędzy instancjami, albo nawet być wymienialna w trakcie życia obiektu.

    Ogólnie patrząc na jakikolwiek wzorzec projektowy warto najpierw zrozumieć jaki problem próbuje on rozwiązać, a nie że jest tam jakaś klasa i interface, bo klasa i interface jest w każdym.

    Jak masz np. klasę, która dostaje jakieś dane i ma zadanie przerobić je na wykres, a tych rodzajów tych wykresów można mieć dużo, to można:
    Zrobić sobie drzewko dziedziczenia pomiędzy jakąś klasą abstrakcyjną, a w klasach dziedziczących zapodać kawałki kodu który przekształca dane w bitmapę z wykresem kołowym, liniowym co tam jeszcze. Albo mieć jedną klasę z wszystkimi rodzajami wykresów, albo właśnie zastosować strategię i wyciągnąć całą zmienną logikę na zewnątrz.
    I owszem, wstrzykiwanie tej logiki, to zawsze będzie jakieś DI, jak logika opiera się wyłącznie na jednej metodzie, to można ten interface zaimplementować lambdą, można do wstrzykiwania używać new, factory method, abstract factory (ktoś kiedyś tego użył?), kontenera IoC.

Porównywanie czegokolwiek do DI, jest o tyle bez sensu, że mając dowolną klasę, która ma jakieś pole, referencja w tym polu musi być "jakoś" wstrzyknięta, więc termin jest na tyle szeroki, że właściwie oznacza wszystko. Dla mnie (może nadinterpretuję) private DbDao db = DbDao().getInstance() też jest DI, tylko zrealizowanym w nienajlepszy sposób.
Zakładając, że chodzi ci o jakieś IoC, czy wstrzykiwanie zależności przez framework/bibliotekę w stylu SomeController(@Inject DbDao dao) to nadal nie ma to nic wspólnego z wzorcem "strategia", bo owszem jakoś tam wstrzykujesz zależność, ale w żaden sposób nie zmieniasz zachowania obiektu, a jedynie źródło danych z którego korzysta.
Najczęściej spotykany przykład użycia strategii to pewnie taki kawałek kodu:

myStringList.Stream()
  .map(s -> s.toLowerCase()) //wstrzyknięcie logiki za pomocą lambdy (czyli implementacja ad hoc interface'u funkcyjnego)
  .collect(Collectors.toList()) //wstrzyknięcie logiki za pomocą implementacji Collector

W obu liniach masz moim zdaniem strategię, chociaż nie zgodną w 100% z proponowaną implementacją, ale za to w 100% realizowany jest cel wzorca. Zwyczajnie trzeba pamiętać, że to co proponują książki zostało napisane kilkadziesiąt lat temu, a proponowane implementacje są często kompletnie nie przystające do dzisiejszych realiów języka. To nie oznacza, że przestał istnieć taki problem jak np. konieczność posiadania w aplikacji pojedynczej instancji jakiegoś obiektu, albo możliwość zmiany zachowania jakiejś jej części.

5

Dlaczego strategia to wzorzec bez sensu (w językach, które mają jakieś wsparcie do fp).

  1. Strategia opiera się na tym, że mamy jakiś interfejs I. W nim mamy metodę np. algorithm. Oczywiście mamy do tego interfejsu ileś "implementacji".
  • wiadomo, że dobry interfejs ma jedną metodę
  • akurat w przypadku strategii to "norma" (jedna metoda)
  1. Dlatego zamiast patrzeć na interfejs postaci:
interface I {
  B algorithm(A a)
}

możemy przyjąć, że jest to po prostu funkcja A->B.
W javie jest to zapisywane jako:
Function<A,B>,
w normalnych językach A->B albo A=>B itp.
Po prostu typ (funkcyjny).
Czyli interfejs (jednometodowy) to po prostu kulawy sposób zapisania typu funkcyjnego (sygnatury).
Czyli interfejs zmieniamy na sygnaturę, a implementacja to po prostu konkretna "lambda".

  1. Gdybyśmy tak zrobili (zamienili interfej na typ funkcyjny), to strategia po prostu przekazywanie funkcji do funkcji. (Higher order function). Rzecz, którą robimy normalnie.
    (właśnie miałem podać przykład z map, ale ktoś mnie ubiegł :-) )

  2. Więcej - na dowolne wartości możemy patrzeć jak na funkcje ( funkcje stałe). W tym sensie, że taka wartość 5 to po prostu funkcja bezargumentowa, która zwraca 5. () -> 5.
    (zresztą przykład z wątku o rekrutacjach w zasadzie tak miał)
    W tym sensie println("hello world") to użycie strategii :-)

  3. Czyli, bardziej ogólnie - każde wywołanie funkcji z argumentami to strategia :-)

  4. Ponieważ, zwykle wywołujemy i piszemy funkcje z argumentami ... prawie cały czas (chyba, że piszemy w BASICU)... to mówienie o tym, że korzystamy z wzorca strategia jest mało pomocne, troche tak, jakbym podkreślał, że napiszę post prozą.

(rozumowanie da się rozszerzyć na interfajsy z wieloma metodami (template method).
Dla ułatwienia, napisałem ten post prozą.

0

Czy w takim razie Strategia to prymitywna nazwa na Higher order function? Albo Strategia to proteza Higher order function w prymitywnych jezykach? IMHO Do tego drugiego bym się skłaniał. Kolejny wzorzec który umarł RIP :P

3

Kolejny wzorzec który umarł RIP :P

Praktycznie wszystkie wzorce behavioralne polegały na symulowaniu higher order function przy pomocy konstrukcji z oop.

Ale generalnie wzorce powinny być martwe, ze względu na to, że polagają na powtarzaniu jakiegoś kawałka kodu (kształtu).
Języki i biblioteki ewoluują, żebyśmy nie musieli się powtarzać (nawet golang :-) ).

1

Otóż nie, strategie możesz łatwo przekazać przez konstruktor np. Map<FileExtension, ReportGenerator>
I przekazać listę implementacji poprzez DI.
Możesz mieć ReportGenerator z metodami generate coś tam coś tam, oraz getSupportedFileExtension na podstawie którego wybierzesz implementację.
O wiele wygodniejsze w tym przypadku niż lambda.
No i ponadto nie jest wykluczone że strategia może mieć stan

1

Otóż:
Map<FileExtension, Data->Raport>

gdzie:
Data->Raport == Function<Data, Raport> == RaportGenerator

Działa też przy DI.

Co do stanu to po prawdzie funkcja w większości języków hybrydowych też może mieć stan, ale to smutny przypadek.

2

Otóż można robić tak, tylko co jeśli implementacja każdego report generatora będzie zajmować po 50 czy 100 linii?
Mam robić w Javie sztuczna klasę ze statycznymi metodami które przekaże jako method reference, albo np.5 sztucznych klas że statycznymi metodami, czy może jednak wygodniej będzie mi zrobić klasy PDFReportGenerator, CSVReportGenerator itd, które w Javie czy C# będą bardziej naturalne?

2
jarekr000000 napisał(a):

Dlaczego strategia to wzorzec bez sensu (w językach, które mają jakieś wsparcie do fp).

  1. Strategia opiera się na tym, że mamy jakiś interfejs I. W nim mamy metodę np. algorithm. Oczywiście mamy do tego interfejsu ileś "implementacji".
  • wiadomo, że dobry interfejs ma jedną metodę
  • akurat w przypadku strategii to "norma" (jedna metoda)
  1. Dlatego zamiast patrzeć na interfejs postaci:
interface I {
  B algorithm(A a)
}

możemy przyjąć, że jest to po prostu funkcja A->B.
W javie jest to zapisywane jako:
Function<A,B>,
w normalnych językach A->B albo A=>B itp.
Po prostu typ (funkcyjny).
Czyli interfejs (jednometodowy) to po prostu kulawy sposób zapisania typu funkcyjnego (sygnatury).
Czyli interfejs zmieniamy na sygnaturę, a implementacja to po prostu konkretna "lambda".

  1. Gdybyśmy tak zrobili (zamienili interfej na typ funkcyjny), to strategia po prostu przekazywanie funkcji do funkcji. (Higher order function). Rzecz, którą robimy normalnie.
    (właśnie miałem podać przykład z map, ale ktoś mnie ubiegł :-) )

  2. Więcej - na dowolne wartości możemy patrzeć jak na funkcje ( funkcje stałe). W tym sensie, że taka wartość 5 to po prostu funkcja bezargumentowa, która zwraca 5. () -> 5.
    (zresztą przykład z wątku o rekrutacjach w zasadzie tak miał)
    W tym sensie println("hello world") to użycie strategii :-)

  3. Czyli, bardziej ogólnie - każde wywołanie funkcji z argumentami to strategia :-)

  4. Ponieważ, zwykle wywołujemy i piszemy funkcje z argumentami ... prawie cały czas (chyba, że piszemy w BASICU)... to mówienie o tym, że korzystamy z wzorca strategia jest mało pomocne, troche tak, jakbym podkreślał, że napiszę post prozą.

(rozumowanie da się rozszerzyć na interfajsy z wieloma metodami (template method).
Dla ułatwienia, napisałem ten post prozą.

Nic bardziej mylnego. Strategia opiera się na tym, że mamy rodzinę wymienialnych algorytmów. To, że jest tam jakiś interfejs, to jak @piotrpo wspomniał truizm, bo w OOP zawsze są gdzieś jakieś klasy i interfejsy.
Pozytywnym skutkiem istnienia tego interfejsu jest to, że ogranicza możliwości wstrzyknięcia konkretnego algorytmu do klienta jedynie do tych implementujących kontrakt, czyli świadomie utworzonych w celu zaimplementowania algorytmu. Nie każda A -> B jest implementacją strategii, która przypadkiem ma taki kontrakt.

Wzorzec to nie jest tylko struktura w kodzie, to jest też cel, zastosowanie, rozwiązywany problem. Wyciąganie jednej ze składowych wzorca (i to tej najmniej istotnej), i budowanie na tym hejtu, to jakaś dziwna logika. Aż przypominają mi się felietony Janusza Korwina Mikkego o tym, że tramwaje są komunistyczne, bo zabierają wolność jeżdżąc tylko po torach i zatrzymując się tylko na przystankach, a nie podjeżdżając pod dom.

0
somekind napisał(a):

Pozytywnym skutkiem istnienia tego interfejsu jest to, że ogranicza możliwości wstrzyknięcia konkretnego algorytmu do klienta jedynie do tych implementujących kontrakt, czyli świadomie utworzonych w celu zaimplementowania algorytmu. Nie każda A -> B jest implementacją strategii, która przypadkiem ma taki kontrakt.

A komuś chce się pisać te interfejsy? Ja widzę coraz większe wypieranie. W bibliotekach znikają jakieś dziwne customowe interfejsy a wypierają je szeroko pojęte funkcje

1

@KamilAdam: Ale wiesz, że te funkcje, to też się definiuje przez interface (w językach obiektowych)?

2

No to pa:

typealias Discount = Int -> Int


//implementacja 1. (można wynieść do osobnego pliku)

val standardDicount : Discount = {x -> x - x/10 } 


//implementacja 2. klasyczna (ala java)
class NoDiscount :  Discount {
  fun apply(x: Int) = x
}


//implementacja 3.  hybrydowa kotlinowa
class WorstDiscountEver : Int  -> Int {
  fun apply(x : Int) = x + x
}


To kotlin, jako przykład dość sensownego wsparcia do fp w języku obiektowym.

@scibi_92: to, że w javie bujałbyś się z jakimiś metodami statycznymi to prawda, ale w sumie robi to niewiele więcej narzutu (jedna linijka ) niż standardowa implementacja (możesz sobie dla kazdego przypadku stworzyć osobny plik z jedną metodą statyczną). Tylko po co pisać w javie?

@somekind: argument dobry, to powyżej zresztą nie do końca jest rozwiązaniem, bo typealias to tylko alias i jak pokazuje przypadek 3 - cokolwiek (Int->Int) będzie pasować, a nazwa Discount pełni tylko funkcję dokumentacyjną. To czasem wada - czasem zaleta.

Aczkolwiek istnieje prosty sposób, zeby jednak wprowadzić nowy typ i tak się robi:

//sygnatura (z nową nazwą)
data class Discount (val alg : Int -> Int) 
//implementacja
val noDiscount = Discount { x -> x } 
val stdDiscount = Discount { x -> x - x/10 }
//kolejne strategie to po prostu instancje typu Discount... a nie implementacje 

w Hasklu byłoby to

--alias
type Discount =  Int -> Int

--nowa nazwa

newtype Discount = Discount ( Int -> Int ) 

-- itd.

Czyli interfejs jednometodowy jest tożsamy z sygnaturą funkcji. Do której możemy ewentualnie wprowadzić dodatkową restrykcję w postaci nazwy typu (nie we wszystkich językach - bo te ze structural typing i tak mają tą nazwę gdzieś (TS) ).
I w językach wspierających fp można po prostu zrezygnować z interfejsów i całej zabawy z nimi.

0

Wiem, ale po co mam tworzyć swój interfejs jak już jest w bibliotece standardowej interfejs któego mogę użyć?
Dodatkowa robota, dodatkowe linie.
Niech będzie przykład ze Scali
Po co tworzyć nowy interfejs Comparator[A]. Jak wystarczy już istniejący (A, A) => Boolean?

1

@KamilAdam: Bo czasami, potrzeba (albo mi się tak wydaje) czegoś więcej niż jedna funkcja, albo nawet więcej niż 5 funkcji pure. Zrób implementację takiego Collector niezależnymi funkcjami.

0
piotrpo napisał(a):

@KamilAdam: Bo czasami, potrzeba (albo mi się tak wydaje) czegoś więcej niż jedna funkcja, albo nawet więcej niż 5 funkcji pure. Zrób implementację takiego Collector niezależnymi funkcjami.

To jest dobry argument. Pytanie czy Collector to Strategia czy już Metoda Szablonowa, bo a Strategii tu rozmawiamy. I czy Strategia może mieć więcej niż jedną metodę publiczną.
Bo przecież nie jestem szaleńcem który chciałby każdy wstrzykiwany obiekt zastąpić skończoną ilością funkcji. Da się, ale to nie ma sensu. Nawet w Haskellu tak się nie robi, tylko używa Type Class do zmiany implementacji. A potem kompilator Haskella zamienia wszystko na skończoną ilość funkcji :D

1

@KamilAdam: IMO strategia. Pomijając sposób implementacji (Interface vs. Abstract Class) metoda szablonowa, albo kilka metod szablonowych jest wywoływanych z poziomu "gospodarza", który robi całą orkiestrację i przekazywanie danych. Tutaj masz jakiś własny model i oczywiście dałoby się go też przekazywać pomiędzy funkcjami za pomocą kodu "gospodarza", ale raczej się tak nie robi.

edit:
Template Method nie widziałem już od lat w niczym nowym. Pewnie dlatego, że faktycznie prościej to zrobić lambdami.

2

@piotrpo
proszz
1.
data class Collector<T,A,R>(val accumulator:(A,T)->(), val combiner: (A,A)->A, val finisher:(A)->R, //itd...

możemy to nawet zupełnie zastąpić funkcją (ale będzie średnie w użyciu):
<A,T>() -> ((A,T)->(), (A,A)->A, (A)->R ....//itd)
2. Collector to przykład dość topornego interfejsu (nie jest najgorszy w javie - bo tu Collection chyba wygrywa), w zasadzie to nawet widać... że to zestaw mniejszych interfejsów (i ten koncept z data class całkiem pasuje - bo można łatwiej współdzielić kod (np. różne kombinacje accumulator i finisher są przy obecnym designie w java.util. utrudnione, do każdej kombinacji trzeba zrobić osobną "klasę" implementującą).

1

Co do tej intencji, to moim zdaniem w kontekście tego wątku

Użycie DI sugeruje że ktoś może chcieć mieć możliwość podmiany bebechów, ale nie musi

A strategii że ktoś już ma wiele różnych bebechów

1

@1a2b3c4d5e: Co rozumiesz pod pojęciem DI? Bo dla mnie to jedynie wstrzyknięcie czegoś gdzieś. Niekoniecznie oznacza, że to co jest wstrzykiwane ma jakąś tam logikę w sobie. Równie dobrze można wstrzyknąć np. strukturę z konfiguracją.

0
KamilAdam napisał(a):

A komuś chce się pisać te interfejsy? Ja widzę coraz większe wypieranie.>

Jeśli chce się mieć ograniczony kontrakt na rodzinę wymienialnych algorytmów jednakowego przeznaczenia to w niektórych językach nie ma wyjścia. Jeśli się tego nie zrobi, to umożliwi się klientowi użycie dowolnej implementacji pasującej do wejścia i wyjścia, nawet najbardziej absurdalnej. Efekt jak przy dynamicznym typowaniu.

W bibliotekach znikają jakieś dziwne customowe interfejsy a wypierają je szeroko pojęte funkcje

I słusznie, biblioteki nie powinny nakładać jakichś dziwnych ograniczeń na swoich użytkowników.

jarekr000000 napisał(a):

Aczkolwiek istnieje prosty sposób, zeby jednak wprowadzić nowy typ i tak się robi:

//sygnatura (z nową nazwą)
data class Discount (val alg : Int -> Int) 
//implementacja
val noDiscount = Discount { x -> x } 
val stdDiscount = Discount { x -> x - x/10 }
//kolejne strategie to po prostu instancje typu Discount... a nie implementacje 

No, fajne. Tak więc nauczyliśmy się dzisiaj, że w językach wspierających FP też istnieją te znienawidzone wzorce projektowe, i wymagają nawet tyle samo kodu. Tylko oczywiście używa się oczywiście innych konstrukcji.

KamilAdam napisał(a):

Wiem, ale po co mam tworzyć swój interfejs jak już jest w bibliotece standardowej interfejs któego mogę użyć?

Możesz dać linka do dokumentacji tej biblioteki standardowej, która zawiera już kod przyjmujący ICollection<Invoice>, ICollection<Customer>, a na wyjściu plik importu przelewów do PKO BP, ING i mBanku?

0
somekind napisał(a):
KamilAdam napisał(a):

A komuś chce się pisać te interfejsy? Ja widzę coraz większe wypieranie.>

Jeśli chce się mieć ograniczony kontrakt na rodzinę wymienialnych algorytmów jednakowego przeznaczenia to w niektórych językach nie ma wyjścia. Jeśli się tego nie zrobi, to umożliwi się klientowi użycie dowolnej implementacji pasującej do wejścia i wyjścia, nawet najbardziej absurdalnej. Efekt jak przy dynamicznym typowaniu.

Czy mam przez to rozumieć sortowanie w Javie jest bezpeczniejsze niż w Scali bo w Javie trzeba zaimplementować specjalny interfejs to tego czyli Comparator<A> a w Scali wystaeczy zaimplementować funkcję (A, A) -> Boolean co po Javowemu byłoby BiFunction<A, A, Integer>?
Przez to nie mogę napisać

BiFunction<Integer, Integer, Boolean> dummyFunction = (i1, i2) -> 0;
Collections.sort(list, dummyFunction);

Ale szczęśliwie dalej mogę napisać:

class FunctionWrapper implement Comparator<A> {
  private BiFunction<A, A, Integer> f;
  public FunctionWrapper(BiFunction<A, A, Integer> f) {
    this.f = f;
  }
  @Override
  public int compare(A a1, A a2) {
    return f.apply(a1, a2)
  }
}

BiFunction<Integer, Integer, Integer> dummyFunction = (i1, i2) -> 0;
Collections.sort(list, new FunctionWrapper(dummyFunction));

Tak, widzę znaczący wzrost bezpieczeństwa :D

BTW ponieważ Java jest dziwna można też napisać

Comparator<Integer> dummycomparator = (i1, i2) -> 0;
Collections.sort(list, dummycomparator);
KamilAdam napisał(a):

Wiem, ale po co mam tworzyć swój interfejs jak już jest w bibliotece standardowej interfejs któego mogę użyć?

Możesz dać linka do dokumentacji tej biblioteki standardowej, która zawiera już kod przyjmujący ICollection<Invoice>, ICollection<Customer>, a na wyjściu plik importu przelewów do PKO BP, ING i mBanku?

W sensie chodzi co o (ICollection<Invoice>, ICollection<Customer>) => PlikImportu ? Czy po Javowemu BiFunction<ICollection<Invoice>, ICollection<Customer>, PlikImportu>

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