Zagnieżdżony obiekt w optionalu i stream

0
public class Master {
    private final Person person;
    ...
}
public class Person {
    private final Business business;
    private final boolean isCorrect;
    ...
}
public class Business {
    private final List<String> businessList;
    ...
}

Mamy taki zagnieżdzony obiekt, chcemy wydostać listę. Chciałbym zrobić coś takiego, że sprawdzam każdorazowo na którym momencie może wyskoczyć null i dodatkowo na końcu czy lista nie jest pusta. Na razie doszedłem do takiego momentu. Jak jeszcze dorzucić sprawdzanie pustej listy? Java 11+, bez bibliotek.

List<String> result = Optional.of(master)
                .map(x -> Optional.ofNullable(x.getPerson()).orElseThrow(() -> new RuntimeException("Jakis pierwszy")))
                .map(x -> Optional.ofNullable(x.getBusiness()).orElseThrow(() -> new RuntimeException("Jakis drugi")))
                .map(x -> x.getBusinessList())
                .stream()
                .flatMap(x -> x.stream())
                .collect(Collectors.toList());
0

Zamiast tych długich mapów, zrób pojedyncze dla każdego, a pomiędzy nimi po prostu dołóż .filter(Objects::nonNull) czy jakoś tak. I potem na końcu możesz w ogóle pozbyć się Twoich dwóch ostatnich linijek bo praktycznie nic nie zmieniają, więc na końcu, jak już zrobisz .map(x -> x.getBusinessList()) to zrób po tym .findFirst() i masz Optionala z listą, więc za tym dołożysz .orElseThrow(() -> new RuntimeException("...")) i jest :)

0
Pinek napisał(a):

Zamiast tych długich mapów, zrób pojedyncze dla każdego, a pomiędzy nimi po prostu dołóż .filter(Objects::nonNull) czy jakoś tak. I potem na końcu możesz w ogóle pozbyć się Twoich dwóch ostatnich linijek bo praktycznie nic nie zmieniają, więc na końcu, jak już zrobisz .map(x -> x.getBusinessList()) to zrób po tym .findFirst() i masz Optionala z listą, więc za tym dołożysz .orElseThrow(() -> new RuntimeException("...")) i jest :)

    List<String> l2 = Optional.of(m)
            .map(x -> x.getPerson())
            .map(x -> x.getBusiness())
            .map(x -> x.getBusinessList())
            .orElseThrow(() -> new RuntimeException("Nie o to mi chodzi :("));
1

Tak troche komętarz zgreda architekta:
Nie rozumiem koncepcji:
Optional.ofNullable(x.getPerson()).orElseThrow(() -> new RuntimeException("Jakis pierwszy"))
Nie wiem po co pisać ten kod jak Java ma to wbudowane od wersji 1.0
Jak się odwołasz do metody na null to poleci NullPointerException i powie Ci gdzie. W najnowszych wersjach powie nawet ładniej.

2

@jarekr000000: @Pinek
Dobra chyba ogarnąłem. Muszę się bardziej opierać na .map(). Tutaj przykład o co mi chodziło mniej więcej o coś takiego:

List<String> l = Optional.of(master)
                .map(x -> Optional.ofNullable(x.getPerson()).orElseThrow(() -> new RuntimeException("Jakis pierwszy"))) // to tylko głupi przykład
                .map(x -> mapPersonToBusiness(x))
                .map(x -> x.getBusinessList())
                .map(x -> validateBusinessListSize(x))
                .orElseThrow(() -> new RuntimeException("Co wy na to"));
private static Business mapPersonToBusiness(final Person person) {
        if (null == person) {
            throw new RuntimeException("Mamy null");
        }
        if (!person.isCorrect()) {
            throw new RuntimeException("Dodatkowa walidacja");
        }
        return person.getBusiness();
    }
private static List<String> validateBusinessListSize(final List<String> businessList) {
        if (businessList.size() == 0) {
            throw new RuntimeException();
        }
        return businessList;
    }

Generalnie chodzi mi o to że na każdym etapie przekształcenia muszę sprawdzić wiele rzeczy które kwalifikują czy mogę przejść dalej. Muszę mieć możliwość poinformowania co się konkretnie mogło wydupić. Nie wiem jeszcze jak sobie to uporządkować ze względu na NullChecki ale to już mniej ważne, pewnie dodatkowy if w każdym mapie (tak jak mapPersonToBusiness(...)). Myślałem o tym orElseThrow tak jak na początku robiłem ale rzeczywiście kiepsko to się czyta.

4

Ten kod jest tragiczny. Po pierwsze na sile fluent API - może tutaj nie pasuje? Jak to będzie wyglądało Java7 way? Po drugie robisz null checki na czymś co w wcześniej sprawdziłeś i rzucasz wyjątkiem jak jest nullem. Po trzecie - defensywne programowanie, mnóstwo null checków - waliduj obiekty przed wpuszczeniem do domeny, dopisz testy.

1

@Charles_Ray ma rację. Fluent API pasuje tu jak pięść do nosa. Mimo że jestem miłośnikiem programowania funkcyjnego zrobiłbym to tak (jeśli już i tak muszę rzucać exceptiony):

var person = Preconditions.checkNotNull(master.getPerson(), "Jakiś pierwszy");
Preconditions. checkArgument(person.isValid(), "Person is not valid");

var business = Precondtions.checkNotNull(person.getBusiness(), "Jakiś drugi");
Preconditions. checkArgument(business.isValid(), "Business is not valid");

return business.getBusinessList()
0

Grzebanie w bebechach łamie prawo Demeter, dlaczego na masterze nie mogłoby być walidacji albo metody getBusinessList()? Idąc dalej może w ogóle nie powinno się dać utworzyć niepoprawnego mastera?

0
Charles_Ray napisał(a):

Grzebanie w bebechach łamie prawo Demeter, dlaczego na masterze nie mogłoby być walidacji albo metody getBusinessList()? Idąc dalej może w ogóle nie powinno się dać utworzyć niepoprawnego mastera?

łamie prawo demeter tylko jeśli uznajemy że master jest obiektem domenowym. Jak to jest tylko struktura którą dostajemy przez resta (dla baz danych mówi się o modelu persystencji, jak nazwać to dla resta? model restowy?) to o programowaniu obiektowy, modelu domenowym i prawie demeter trudno tu mówić?
A może to właśnie jest walidacja danych przed utworzeniem modelu domenowego tylko OP nie potrafi tego wyrazić?
A może moduł jest tak damy że nie ma prawdziwego modelu domenowego bo to skrypt (w rozumieniu DDD), a nie prawdziwa aplikacja (w rozumieniu DDD)?
Tak dużo pytań, tak mało odpowiedzi

0

To tak z bani po kilku piwach:

Optional.of(master)
    .flatMap(Master::getPerson)
    .flatMap(Person::getBusiness)
    .faltMap(Business::businessList)
    .getOrElse(Collections::emptyList);

Chyba, że musisz rzucać wyjątkami.

1

A tak po prawdzie to z tego co piszesz to chcesz mieć kod taki jaki wyżej od @Koziołek
Tylko zamiast Optionali wrzucasz Either i przestajesz bawić się w te wyjątki - one nie pasują Ci do koncepcji.

0

@jarekr000000: na czysto bez Vavr, ale twój pomysł jest całkiem fajny (o ile ktoś chce się tego nauczyć).

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