"Zduplikowana" metoda różniąca się parametrem i wywoływanymi metodami - refactoring

"Zduplikowana" metoda różniąca się parametrem i wywoływanymi metodami - refactoring
T9
  • Rejestracja:ponad 13 lat
  • Ostatnio:ponad 3 lata
  • Postów:48
0

Cześć,

Mam w kodzie napisaną metodę która przyjmuje kilka parametrów praktycznie takich samych. Jeden parametr się tylko pomiędzy nimi różni - lista przyjmuje różne obiekty.
Tych metod jest kilkanaście, dla każdego typu obiektu oddzielna, wykonuje praktycznie to samo, różni się tylko, że w każdej z tych metod wywoływana jest inna metoda dedykowana pod konkretny typ obiektu, wygląda to w uproszczeniu tak:

Kopiuj
private Integer generujTyp(List<Typ> lista) {
    if (lista.isEmpty() || lista.size() == 1) {
		{...} //elementy stałe każdej metody
        uzupelnijDaneTyp(tabela, lista.isEmpty() ? null : lista.get(0));            
    } else {
        for (Typ typVo : lista) {
                    {...} - //elementy stałe każdej metody
            uzupelnijDaneTyp(tabela, typVo);
        }
    }
}

Chciałbym zrefaktorować kod pozbywając się tych metod i zastąpić ją jedną. Nie miałbym żadnego problemu, gdyby nie to, że muszę wywoływać specjalne metody (uzupelnijDaneTyp dla Typ, uzupelnijDaneTyp2 dla Typ2 itd).
Zastanawiam się, jak to najlepiej zrobić.... Myślałem, żeby każdy typ obiektu dziedziczył po tej samej abstrakcyjnej klasie i miał własną implementację metody uzupelnijDane gdzie będzie wywoływana konkretna i właściwa metoda. Tylko, że powstaje problem, bo nie zależnie od zawartości listy to uzupelnijDaneTyp musi być wywołane zawsze..

Innym sposobem to myślałem, żeby badać instanceof i wywoływac właściwe metody, ale nie podoba mi się to zbytnio...

Ktoś ma pomysł jak zrefaktorować ten kod? :)

MrMadMatt
  • Rejestracja:ponad 9 lat
  • Ostatnio:około 22 godziny
  • Postów:373
0

Jakbyś mógł pokazać większy kawałek kontekstu / kodu to łatwiej byłoby zrozumieć twoją sytuację. ;) Pokaż ze dwie/trzy takie metody.

edytowany 1x, ostatnio: MrMadMatt
KamilAdam
  • Rejestracja:ponad 6 lat
  • Ostatnio:5 dni
  • Lokalizacja:Silesia/Marki
  • Postów:5505
0

Jeśli parametry różnią się tylko generykiem listy to instanceof nie zadziała. Generyki są wymazywane podczas kompilacji.
Jeśli metoda uzupelnijDaneTyp znajdowała by się w klasie AbstractTyp to możnaby to zmienić na:

Kopiuj
private void generujTyp(List<AbstractTyp> lista) {
   lista.foreach(typ -> typ.uzupelnijDaneTyp());
}

Mama called me disappointment, Papa called me fat
Każdego eksperta można zastąpić backendowcem który ma się douczyć po godzinach. Tak zostałem ekspertem AI, Neo4j i Nest.js . Przez mianowanie
Shalom
  • Rejestracja:ponad 21 lat
  • Ostatnio:około 3 lata
  • Lokalizacja:Space: the final frontier
  • Postów:26433
0

O ile cię dobrze rozumiem, to czemu nie wrzucisz tam FUNKCJI jako parametru do tej metody? :) Ewentualnie mapy z funkcjami, po jednej dla każdego typu. Wyciągasz taką funkcje i odpalasz.


"Nie brookliński most, ale przemienić w jasny, nowy dzień najsmutniejszą noc - to jest dopiero coś!"
edytowany 1x, ostatnio: Shalom
jarekr000000
  • Rejestracja:ponad 8 lat
  • Ostatnio:około godziny
  • Lokalizacja:U krasnoludów - pod górą
  • Postów:4709
2

Przecież twój kod to odpowiednik

Kopiuj
lista.stream().forEach( typVo -> 
            {...} - //elementy stałe każdej metody
            uzupelnijDaneTyp(tabela, typVo);
);

Z jednym tylko wyjątkiem, że dla pustej listy nie wywołujemy tego durnego pomysłu -> uzupelnijDaneTyp(tabela, null);
Ale możliwe, że wcale i tak nie chcesz tego wywoływać.

Możesz metodę zastąpić taką:

Kopiuj
private Integer generujTyp(List<Typ> lista, Bifunction<Tabela, Typ> func  ) {
         lista.stream().forEach( typVo -> 
            {...} - //elementy stałe każdej metody
            func.apply(tabela, typVo);
}

i wtedy wywołanie to:

Kopiuj
 generujTyp(lista, X::uzupelnijDaneTyp);

jeden i pół terabajta powinno wystarczyć każdemu
edytowany 1x, ostatnio: jarekr000000
PU
lista.stream().forEach() -> lista.forEach() drobiazg :)
T9
  • Rejestracja:ponad 13 lat
  • Ostatnio:ponad 3 lata
  • Postów:48
0

@MrMadMatt metody te różnią typem listy i wywoływaną metodą:

Kopiuj
private Integer generujTyp2(List<Typ2> lista) {
    if (lista.isEmpty() || lista.size() == 1) {
        {...} //elementy stałe każdej metody
        uzupelnijDaneTyp2(tabela, lista.isEmpty() ? null : lista.get(0));            
    } else {
        for (Typ2 typVo : lista) {
                    {...} - //elementy stałe każdej metody
            uzupelnijDaneTyp2(tabela, typVo);
        }
    }
}

Powyżej umieściłem drugi przykład metody - różni się TYP - TYP2 i uzupelnijDaneTyp - uzupelnijDaneTyp2 i tak kilkanaście metod.

@Kamil Żabiński myślałem właśnie nad takim rozwiązaniem, z tymże zwróć uwagę, że metoda ma być wywołana nie zależnie od zawartości listy. Jeśli lista ma n>1 elementów, to dla każdego z elementu, a jeśli 1 to dla jednego. Jeśli lista jest pusta, metoda też jest wywoływana tylko z parametrem null. Rozróżnienie czy lista jest pusta lub ma jeden obiekt a kilka obiektów jest potrzebne, bo metody generujTyp, generujTyp2 itd... realizuje też inne procesy biznesowe które są takie same w każdej z tych metod. Proces biznesowy dla pustej listy i listy z jednym elementem jest inny niż dla listy z kilkoma obiektami, lecz w każdej metodzie generujTyp taki sam, co powoduje dużą duplikację kodu...

edytowany 1x, ostatnio: turo90
KamilAdam
  • Rejestracja:ponad 6 lat
  • Ostatnio:5 dni
  • Lokalizacja:Silesia/Marki
  • Postów:5505
0

Jak zwykle null psuje wszystko. Nie na się zrobić polimorfizmu po nullu. Musiałbyś najpierw postarać się wydzielić przypadek pustej listy


Mama called me disappointment, Papa called me fat
Każdego eksperta można zastąpić backendowcem który ma się douczyć po godzinach. Tak zostałem ekspertem AI, Neo4j i Nest.js . Przez mianowanie
T9
  • Rejestracja:ponad 13 lat
  • Ostatnio:ponad 3 lata
  • Postów:48
0

@jarekr000000: no właśnie o to chodzi, że dla pustej listy metoda też musi zostać wywołana... można to jakoś zrefaktorować, ale docelowo i tak musi być wywołana, bo nie zależnie czy dostanie nulla czy właściwy obiekt to ona wykonuje pewne działania, które muszą być wykonane, więc nie bardzo mogę zastosować lista.stream().forEach(....).

@Kamil Żabiński przekazywanie nulla nie jest konieczne, zamiast tego może być wywołana inna metoda, która będzie dla wszystkich przypadków pustej listy taka sama.

MrMadMatt
  • Rejestracja:ponad 9 lat
  • Ostatnio:około 22 godziny
  • Postów:373
0

@turo90:
A nie mógłbyś zrobić tego co zaproponował @jarekr000000 w taki sposób jak poniżej?

Kopiuj
private Integer generujTyp(List<Typ> lista, Bifunction<Tabela, Typ> func  ) {

    return (lista.isEmpty() || lista.size() == 1) ?
        func.apply(tabela, typVo);
        : lista.stream().forEach( typVo -> 
            {...} - //elementy stałe każdej metody
            func.apply(tabela, typVo);
}
YA
  • Rejestracja:około 10 lat
  • Ostatnio:około godziny
  • Postów:2371
0

Czy w tym Twoim bloku:

Kopiuj
private Integer generujTyp2(List<Typ2> lista) {
    if (lista.isEmpty() || lista.size() == 1) {
        {...} //elementy stałe każdej metody					<--- logic1();
        uzupelnijDaneTyp2(tabela, lista.isEmpty() ? null : lista.get(0));            
    } else {
        for (Typ2 typVo : lista) {
          {...} - //elementy stałe każdej metody			    <--- logic2();
          uzupelnijDaneTyp2(tabela, typVo);
        }
    }
}
  • logic1() i logic2() są odmienne? Tzn. jeśli lista zawiera więcej niż 1 element, to jest przetwarzana w specjalny sposób? Coś mi tu nie gra, bo skoro mam N elementów do przetworzenia, to N=1 czy N=2 nie powinno robić specjalnej różnicy.
  • logic1() zależy od elementów listy, czy elementy listy (lub ich brak) mają znaczenie tylko dla uzpelnijDaneTypX ?
  • logic2() zależy od typVO ? Czy ten typVO jest tylko na potrzeby uzpelnijDaneTypX ?
T9
  • Rejestracja:ponad 13 lat
  • Ostatnio:ponad 3 lata
  • Postów:48
0

@yarel: Tak logic1() i logic2() są odmienne. Dla listy z 1 elementem logic1() realizuje co innego niż dla listy z kilkoma elementami logic2(). Logic1() zależy od ilości elementów w liście. Po wykonaniu logic1() czy logic2() zawsze musi być wykonane jeszcze uzupelnijDaneTypX które jest zależne od typu obiektu listy. Dla każdego typu (Typ1, Typ2, TypN... jest inna metoda - uzupelnijDaneTyp1, uzupelnijDaneTyp2, uzupelnijDaneTypN. Logic2() jak i logic1() nie zależą od typu listy i w każdej z tych metod uzupelnijDaneTyp1, uzupelnijDaneTyp2 itd.. są takie same, z tymże logic1() != logic2().
Co jeszcze istotne, "tabela" tworzona jest przez logic1() i przez logic2() - różni się sposób tworzenia, więc w funkcji tego przekazać nie mogę.

edytowany 1x, ostatnio: turo90
Shalom
  • Rejestracja:ponad 21 lat
  • Ostatnio:około 3 lata
  • Lokalizacja:Space: the final frontier
  • Postów:26433
1

Napisz nam minimalny kompilujący się i działający przykład który obrazuje twój problem...


"Nie brookliński most, ale przemienić w jasny, nowy dzień najsmutniejszą noc - to jest dopiero coś!"

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.